Merge NAF4 MeshMS Java API into development

Currently implements "list conversations" and "list messages"
This commit is contained in:
Andrew Bettison 2014-06-19 11:21:37 +09:30
commit b14c58bdf6
25 changed files with 1946 additions and 106 deletions

View File

@ -667,6 +667,7 @@ static int _parse_authorization(struct http_request *r, struct http_client_autho
char buf[bufsz];
if (_parse_authorization_credentials_basic(r, &auth->credentials.basic, buf, bufsz)) {
auth->scheme = BASIC;
_commit(r); // make room for following reservations
if ( !_reserve_str(r, &auth->credentials.basic.user, auth->credentials.basic.user)
|| !_reserve_str(r, &auth->credentials.basic.password, auth->credentials.basic.password)
)
@ -974,6 +975,8 @@ static int http_request_parse_header(struct http_request *r)
_commit(r);
return 0;
}
if (r->response.result_code)
return r->response.result_code;
goto malformed;
}
_rewind(r);

View File

@ -0,0 +1,70 @@
/**
* Copyright (C) 2014 Serval Project Inc.
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.codec;
import java.lang.StringBuilder;
public class Base64 {
public static final char[] SYMBOLS = {
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
'='
};
public static String encode(byte[] binary)
{
StringBuilder sb = new StringBuilder();
int place = 0;
byte buf = 0;
for (byte b: binary) {
switch (place) {
case 0:
sb.append(SYMBOLS[b >>> 2]);
buf = (byte)((b << 4) & 0x3f);
place = 1;
break;
case 1:
sb.append(SYMBOLS[(b >>> 4) | buf]);
buf = (byte)((b << 2) & 0x3f);
place = 2;
break;
case 2:
sb.append(SYMBOLS[(b >>> 6) | buf]);
sb.append(SYMBOLS[b & 0x3f]);
place = 0;
break;
}
}
if (place != 0)
sb.append(SYMBOLS[buf]);
switch (place) {
case 1:
sb.append(SYMBOLS[64]);
case 2:
sb.append(SYMBOLS[64]);
}
return sb.toString();
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright (C) 2014 Serval Project Inc.
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.json;
/**
* Thrown when there is any problem with JSON input. This exception class is subclassed to
* specialise it to specific causes, such as JSON syntax error, unexpected JSON token, etc.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
public class JSONInputException extends Exception
{
public JSONInputException(String message) {
super(message);
}
public JSONInputException(Throwable cause) {
super(cause);
}
public JSONInputException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,459 @@
/**
* Copyright (C) 2014 Serval Project Inc.
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.json;
import java.lang.StringBuilder;
import java.lang.NumberFormatException;
import java.io.IOException;
import java.io.Reader;
import java.io.PushbackReader;
import java.util.Collection;
public class JSONTokeniser {
PushbackReader reader;
Object pushedToken;
private static final boolean DUMP_JSON_TO_STDERR = false;
public enum Token {
START_OBJECT,
END_OBJECT,
START_ARRAY,
END_ARRAY,
COMMA,
COLON,
NULL,
EOF
};
public static class SyntaxException extends JSONInputException
{
public SyntaxException(String message) {
super(message);
}
}
public static class UnexpectedException extends JSONInputException
{
public UnexpectedException(String got, Class expecting) {
super("unexpected " + got + ", expecting " + expecting.getName());
}
public UnexpectedException(String got, Object expecting) {
super("unexpected " + got + ", expecting " + jsonTokenDescription(expecting));
}
}
public static class UnexpectedEOFException extends UnexpectedException
{
public UnexpectedEOFException(Class expecting) {
super("EOF", expecting);
}
public UnexpectedEOFException(Object expecting) {
super("EOF", expecting);
}
}
public static class UnexpectedTokenException extends UnexpectedException
{
public UnexpectedTokenException(Object got, Class expecting) {
super(jsonTokenDescription(got), expecting);
}
public UnexpectedTokenException(Object got, Object expecting) {
super(jsonTokenDescription(got), expecting);
}
}
// Can accept any PushbackReader, because we only need one character of unread().
public JSONTokeniser(PushbackReader pbrd)
{
reader = pbrd;
}
public JSONTokeniser(Reader rd)
{
reader = new PushbackReader(rd);
}
private int _read() throws IOException
{
int n = this.reader.read();
if (DUMP_JSON_TO_STDERR && n != -1)
System.err.print((char)n);
return n;
}
private int _read(char[] buf, int offset, int length) throws IOException
{
int n = this.reader.read(buf, offset, length);
if (DUMP_JSON_TO_STDERR && n != -1)
System.err.print(new String(buf, offset, n));
return n;
}
public static void match(Object tok, Token exactly) throws SyntaxException
{
if (tok != exactly)
throw new SyntaxException("JSON syntax error: expecting " + exactly + ", got " + jsonTokenDescription(tok));
}
public void consume(Token exactly) throws SyntaxException, UnexpectedException, IOException
{
match(nextToken(), exactly);
}
public enum Narrow {
NO_NULL,
ALLOW_NULL
};
public static <T> T narrow(Object tok, Class<T> cls) throws UnexpectedException
{
return narrow(tok, cls, Narrow.NO_NULL);
}
@SuppressWarnings("unchecked")
public static <T> T narrow(Object tok, Class<T> cls, Narrow opts) throws UnexpectedException
{
assert !cls.isAssignableFrom(Token.class); // can only narrow to values
if (tok == Token.EOF)
throw new UnexpectedEOFException(cls);
if (opts == Narrow.ALLOW_NULL && (tok == null || tok == Token.NULL))
return null;
if (tok instanceof Token)
throw new UnexpectedTokenException(tok, cls);
// Convert:
// Integer --> Float or Double
// Float --> Double
// Double --> Float
if (cls == Double.class && (tok instanceof Float || tok instanceof Integer))
tok = new Double(((Number)tok).doubleValue());
else if (cls == Float.class && (tok instanceof Double || tok instanceof Integer))
tok = new Float(((Number)tok).floatValue());
if (cls.isInstance(tok))
return (T)tok; // unchecked cast
throw new UnexpectedTokenException(tok, cls);
}
public <T> T consume(Class<T> cls) throws SyntaxException, UnexpectedException, IOException
{
return consume(cls, Narrow.NO_NULL);
}
public <T> T consume(Class<T> cls, Narrow opts) throws SyntaxException, UnexpectedException, IOException
{
return narrow(nextToken(), cls, opts);
}
public Object consume() throws SyntaxException, UnexpectedException, IOException
{
return consume(Object.class, Narrow.NO_NULL);
}
public Object consume(Narrow opts) throws SyntaxException, UnexpectedException, IOException
{
return consume(Object.class, opts);
}
public String consume(String exactly) throws SyntaxException, UnexpectedException, IOException
{
String tok = consume(String.class);
if (tok.equals(exactly))
return tok;
throw new UnexpectedTokenException(tok, exactly);
}
public int consumeArray(Collection<Object> collection, Narrow opts) throws SyntaxException, UnexpectedException, IOException
{
return consumeArray(collection, Object.class, opts);
}
public <T> int consumeArray(Collection<T> collection, Class<T> cls) throws SyntaxException, UnexpectedException, IOException
{
return consumeArray(collection, cls, Narrow.NO_NULL);
}
public <T> int consumeArray(Collection<T> collection, Class<T> cls, Narrow opts) throws SyntaxException, UnexpectedException, IOException
{
int added = 0;
consume(Token.START_ARRAY);
Object tok = nextToken();
if (tok != Token.END_ARRAY) {
while (true) {
collection.add(narrow(tok, cls, opts));
++added;
tok = nextToken();
if (tok == Token.END_ARRAY)
break;
match(tok, Token.COMMA);
tok = nextToken();
}
}
return added;
}
public void consumeArray(Object[] array) throws SyntaxException, UnexpectedException, IOException
{
consumeArray(array, Object.class, Narrow.NO_NULL);
}
public void consumeArray(Object[] array, Narrow opts) throws SyntaxException, UnexpectedException, IOException
{
consumeArray(array, Object.class, opts);
}
public <T> void consumeArray(T[] array, Class<T> cls, Narrow opts) throws SyntaxException, UnexpectedException, IOException
{
consume(Token.START_ARRAY);
for (int i = 0; i < array.length; ++i) {
if (i != 0)
consume(Token.COMMA);
array[i] = consume(cls, opts);
}
consume(Token.END_ARRAY);
}
public static boolean jsonIsToken(Object tok)
{
return tok instanceof Token || tok instanceof String || tok instanceof Double || tok instanceof Integer || tok instanceof Boolean;
}
public static String jsonTokenDescription(Object tok)
{
if (tok == null)
return "null";
if (tok instanceof String)
return "\"" + tok + "\"";
if (tok instanceof Number)
return "" + tok;
if (tok instanceof Boolean)
return "" + tok;
assert tok instanceof Token;
return tok.toString();
}
private void readWord(String word) throws SyntaxException, IOException
{
int len = 0;
while (len < word.length()) {
char[] buf = new char[word.length() - len];
int n = _read(buf, 0, buf.length);
if (n == -1)
throw new SyntaxException("EOF in middle of \"" + word + "\"");
for (int i = 0; i < n; ++i)
if (buf[i] != word.charAt(len++))
throw new SyntaxException("expecting \"" + word + "\"");
}
}
private int readHex(int digits) throws SyntaxException, IOException
{
char[] buf = new char[digits];
int len = 0;
while (len < buf.length) {
int n = _read(buf, len, buf.length - len);
if (n == -1)
throw new SyntaxException("EOF in middle of " + digits + " hex digits");
len += n;
}
String hex = new String(buf);
try {
return Integer.valueOf(hex, 16);
}
catch (NumberFormatException e) {
throw new SyntaxException("expecting " + digits + " hex digits, got \"" + hex + "\"");
}
}
public void pushToken(Object tok)
{
assert jsonIsToken(tok);
assert pushedToken == null;
pushedToken = tok;
}
public Object nextToken() throws SyntaxException, IOException
{
if (pushedToken != null) {
Object tok = pushedToken;
pushedToken = null;
return tok;
}
while (true) {
int c = _read();
switch (c) {
case -1:
return Token.EOF;
case '\t':
case '\r':
case '\n':
case ' ':
break;
case '{':
return Token.START_OBJECT;
case '}':
return Token.END_OBJECT;
case '[':
return Token.START_ARRAY;
case ']':
return Token.END_ARRAY;
case ',':
return Token.COMMA;
case ':':
return Token.COLON;
case 't':
this.reader.unread(c);
readWord("true");
return Boolean.TRUE;
case 'f':
this.reader.unread(c);
readWord("false");
return Boolean.FALSE;
case 'n':
this.reader.unread(c);
readWord("null");
return Token.NULL;
case '"': {
StringBuilder sb = new StringBuilder();
boolean slosh = false;
while (true) {
c = _read();
if (c == -1)
throw new SyntaxException("unexpected EOF in JSON string");
if (slosh) {
switch (c) {
case '"': case '/': case '\\': sb.append((char)c); break;
case 'b': sb.append('\b'); break;
case 'f': sb.append('\f'); break;
case 'n': sb.append('\n'); break;
case 'r': sb.append('\r'); break;
case 't': sb.append('\t'); break;
case 'u': sb.append((char)readHex(4)); break;
default: throw new SyntaxException("malformed JSON string");
}
slosh = false;
}
else {
switch (c) {
case '"':
return sb.toString();
case '\\':
slosh = true;
break;
default:
sb.append((char)c);
break;
}
}
}
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-': {
StringBuilder sb = new StringBuilder();
if (c == '-') {
sb.append((char)c);
c = _read();
}
if (c == '0') {
sb.append((char)c);
c = _read();
}
else if (Character.isDigit(c)) {
do {
sb.append((char)c);
c = _read();
}
while (Character.isDigit(c));
}
else
throw new SyntaxException("malformed JSON number");
boolean isfloat = false;
if (c == '.') {
isfloat = true;
sb.append((char)c);
c = _read();
if (c == -1)
throw new SyntaxException("unexpected EOF in JSON number");
if (!Character.isDigit(c))
throw new SyntaxException("malformed JSON number");
do {
sb.append((char)c);
c = _read();
}
while (Character.isDigit(c));
}
if (c == 'e' || c == 'E') {
isfloat = true;
sb.append((char)c);
c = _read();
if (c == '+' || c == '-') {
sb.append((char)c);
c = _read();
}
if (c == -1)
throw new SyntaxException("unexpected EOF in JSON number");
if (!Character.isDigit(c))
throw new SyntaxException("malformed JSON number");
do {
sb.append((char)c);
c = _read();
}
while (Character.isDigit(c));
}
this.reader.unread(c);
String number = sb.toString();
try {
if (isfloat)
return Double.parseDouble(number);
else
return Integer.parseInt(number);
}
catch (NumberFormatException e) {
throw new SyntaxException("malformed JSON number: " + number);
}
}
default:
throw new SyntaxException("malformed JSON: '" + (char)c + "'");
}
}
}
public void close() throws IOException
{
this.reader.close();
}
}

View File

@ -0,0 +1,131 @@
/**
* Copyright (C) 2014 Serval Project Inc.
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.servaldna;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.HttpURLConnection;
import org.servalproject.codec.Base64;
import org.servalproject.servaldna.SubscriberId;
import org.servalproject.servaldna.ServalDCommand;
import org.servalproject.servaldna.ServalDInterfaceException;
import org.servalproject.servaldna.meshms.MeshMSConversationList;
import org.servalproject.servaldna.meshms.MeshMSMessageList;
import org.servalproject.servaldna.meshms.MeshMSException;
public class ServalDClient implements ServalDHttpConnectionFactory
{
private static final String restfulUsername = "ServalDClient";
private static final String restfulPasswordDefault = "u6ng^ues%@@SabLEEEE8";
private static String restfulPassword;
protected boolean connected;
int httpPort;
public static ServalDClient newServalDClient()
{
return new ServalDClient();
}
protected ServalDClient()
{
restfulPassword = null;
connected = false;
httpPort = 0;
}
private void connect() throws ServalDInterfaceException
{
ensureServerRunning();
if (!connected) {
if (!fetchRestfulAuthorization())
createRestfulAuthorization();
connected = true;
}
}
private void ensureServerRunning() throws ServalDInterfaceException
{
ServalDCommand.Status s = ServalDCommand.serverStatus();
if (!s.status.equals("running"))
throw new ServalDInterfaceException("server is not running");
if (s.httpPort < 1 || s.httpPort > 65535)
throw new ServalDInterfaceException("invalid HTTP port number: " + s.httpPort);
httpPort = s.httpPort;
}
private boolean fetchRestfulAuthorization() throws ServalDInterfaceException
{
restfulPassword = ServalDCommand.getConfigItem("rhizome.api.restful.users." + restfulUsername + ".password");
return restfulPassword != null;
}
private void createRestfulAuthorization() throws ServalDInterfaceException
{
ServalDCommand.setConfigItem("rhizome.api.restful.users." + restfulUsername + ".password", restfulPasswordDefault);
ServalDCommand.configSync();
if (!fetchRestfulAuthorization())
throw new ServalDInterfaceException("restful password not set");
}
public MeshMSConversationList meshmsListConversations(SubscriberId sid) throws ServalDInterfaceException, IOException, MeshMSException
{
MeshMSConversationList list = new MeshMSConversationList(this, sid);
list.connect();
return list;
}
public MeshMSMessageList meshmsListMessages(SubscriberId sid1, SubscriberId sid2) throws IOException, ServalDInterfaceException, MeshMSException
{
MeshMSMessageList list = new MeshMSMessageList(this, sid1, sid2);
list.connect();
return list;
}
// interface ServalDHttpConnectionFactory
public HttpURLConnection newServalDHttpConnection(String path) throws ServalDInterfaceException, IOException
{
connect();
assert restfulPassword != null;
assert httpPort != 0;
URL url = new URL("http", "localhost", httpPort, path);
URLConnection uconn = url.openConnection();
HttpURLConnection conn;
try {
conn = (HttpURLConnection) uconn;
}
catch (ClassCastException e) {
throw new ServalDInterfaceException("URL.openConnection() returned a " + uconn.getClass().getName() + ", expecting a HttpURLConnection", e);
}
int status = 0;
conn.setAllowUserInteraction(false);
try {
conn.addRequestProperty("Authorization", "Basic " + Base64.encode((restfulUsername + ":" + restfulPassword).getBytes("US-ASCII")));
}
catch (UnsupportedEncodingException e) {
throw new ServalDInterfaceException("invalid RESTful password", e);
}
return conn;
}
}

View File

@ -26,7 +26,7 @@ package org.servalproject.servaldna;
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
public class ServalDFailureException extends Exception
public class ServalDFailureException extends ServalDInterfaceException
{
private static final long serialVersionUID = 1L;

View File

@ -0,0 +1,30 @@
/**
* Copyright (C) 2014 Serval Project Inc.
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.servaldna;
import java.io.IOException;
import java.net.HttpURLConnection;
public interface ServalDHttpConnectionFactory {
public HttpURLConnection newServalDHttpConnection(String path) throws ServalDInterfaceException, IOException;
}

View File

@ -0,0 +1,44 @@
/**
* Copyright (C) 2014 Serval Project Inc.
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.servaldna;
/**
* Thrown when the Serval DNA interface has not behaved as expected. This is a general class of
* errors, and is specialised by subclasses that represent an error returned by a server command,
* MDP protocol non-compliance, etc.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
public class ServalDInterfaceException extends Exception
{
public ServalDInterfaceException(String message) {
super(message);
}
public ServalDInterfaceException(Throwable cause) {
super(cause);
}
public ServalDInterfaceException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,72 @@
/**
* Copyright (C) 2014 Serval Project Inc.
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.servaldna.meshms;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import org.servalproject.servaldna.ServalDInterfaceException;
import org.servalproject.json.JSONTokeniser;
import org.servalproject.json.JSONInputException;
class MeshMSCommon
{
protected static JSONTokeniser connectMeshMSRestful(HttpURLConnection conn) throws IOException, ServalDInterfaceException, MeshMSException
{
conn.connect();
if (!conn.getContentType().equals("application/json"))
throw new ServalDInterfaceException("unexpected HTTP Content-Type: " + conn.getContentType());
if (conn.getResponseCode() == HttpURLConnection.HTTP_FORBIDDEN) {
JSONTokeniser json = new JSONTokeniser(new InputStreamReader(conn.getErrorStream(), "US-ASCII"));
try {
json.consume(JSONTokeniser.Token.START_OBJECT);
json.consume("http_status_code");
json.consume(JSONTokeniser.Token.COLON);
json.consume(Integer.class);
json.consume(JSONTokeniser.Token.COMMA);
json.consume("http_status_message");
json.consume(JSONTokeniser.Token.COLON);
String message = json.consume(String.class);
json.consume(JSONTokeniser.Token.COMMA);
json.consume("meshms_status_code");
json.consume(JSONTokeniser.Token.COLON);
int meshms_status = json.consume(Integer.class);
json.consume(JSONTokeniser.Token.END_OBJECT);
json.consume(JSONTokeniser.Token.EOF);
switch (meshms_status) {
case 2:
throw new MeshMSUnknownIdentityException(conn.getURL());
case 3:
throw new MeshMSProtocolFaultException(conn.getURL());
}
throw new ServalDInterfaceException("unexpected MeshMS status = " + meshms_status + ", \"" + message + "\"");
}
catch (JSONInputException e) {
throw new ServalDInterfaceException("malformed response body for HTTP status code " + conn.getResponseCode(), e);
}
}
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK)
throw new ServalDInterfaceException("unexpected HTTP response code: " + conn.getResponseCode());
JSONTokeniser json = new JSONTokeniser(new InputStreamReader(conn.getInputStream(), "US-ASCII"));
return json;
}
}

View File

@ -0,0 +1,46 @@
/**
* Copyright (C) 2014 Serval Project Inc.
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.servaldna.meshms;
import org.servalproject.servaldna.SubscriberId;
public class MeshMSConversation {
public final int _rowNumber;
public final int _id;
public final SubscriberId mySid;
public final SubscriberId theirSid;
public final boolean isRead;
public final int lastMessageOffset;
public final int readOffset;
protected MeshMSConversation(int rowNumber, int _id, SubscriberId my_sid, SubscriberId their_sid, boolean read, int last_message_offset, int read_offset)
{
this._rowNumber = rowNumber;
this._id = _id;
this.mySid = my_sid;
this.theirSid = their_sid;
this.isRead = read;
this.lastMessageOffset = last_message_offset;
this.readOffset = read_offset;
}
}

View File

@ -0,0 +1,165 @@
/**
* Copyright (C) 2014 Serval Project Inc.
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.servaldna.meshms;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Vector;
import java.net.HttpURLConnection;
import org.servalproject.servaldna.ServalDHttpConnectionFactory;
import org.servalproject.servaldna.ServalDInterfaceException;
import org.servalproject.servaldna.SubscriberId;
import org.servalproject.json.JSONTokeniser;
import org.servalproject.json.JSONInputException;
public class MeshMSConversationList {
private ServalDHttpConnectionFactory httpConnector;
private SubscriberId sid;
private HttpURLConnection httpConnection;
private JSONTokeniser json;
private Vector<String> headers;
int columnIndex__id;
int columnIndex_my_sid;
int columnIndex_their_sid;
int columnIndex_read;
int columnIndex_last_message;
int columnIndex_read_offset;
int rowCount;
public MeshMSConversationList(ServalDHttpConnectionFactory connector, SubscriberId sid)
{
this.httpConnector = connector;
this.sid = sid;
}
public boolean isConnected()
{
return this.json != null;
}
public void connect() throws IOException, ServalDInterfaceException, MeshMSException
{
try {
columnIndex__id = -1;
columnIndex_my_sid = -1;
columnIndex_their_sid = -1;
columnIndex_read = -1;
columnIndex_last_message = -1;
columnIndex_read_offset = -1;
rowCount = 0;
httpConnection = httpConnector.newServalDHttpConnection("/restful/meshms/" + sid.toHex() + "/conversationlist.json");
json = MeshMSCommon.connectMeshMSRestful(httpConnection);
json.consume(JSONTokeniser.Token.START_OBJECT);
json.consume("header");
json.consume(JSONTokeniser.Token.COLON);
headers = new Vector<String>();
json.consumeArray(headers, String.class);
if (headers.size() < 1)
throw new ServalDInterfaceException("empty JSON headers array");
for (int i = 0; i < headers.size(); ++i) {
String header = headers.get(i);
if (header.equals("_id"))
columnIndex__id = i;
else if (header.equals("my_sid"))
columnIndex_my_sid = i;
else if (header.equals("their_sid"))
columnIndex_their_sid = i;
else if (header.equals("read"))
columnIndex_read = i;
else if (header.equals("last_message"))
columnIndex_last_message = i;
else if (header.equals("read_offset"))
columnIndex_read_offset = i;
}
if (columnIndex__id == -1)
throw new ServalDInterfaceException("missing JSON column: _id");
if (columnIndex_my_sid == -1)
throw new ServalDInterfaceException("missing JSON column: my_sid");
if (columnIndex_their_sid == -1)
throw new ServalDInterfaceException("missing JSON column: their_sid");
if (columnIndex_read == -1)
throw new ServalDInterfaceException("missing JSON column: read");
if (columnIndex_last_message == -1)
throw new ServalDInterfaceException("missing JSON column: last_message");
if (columnIndex_read_offset == -1)
throw new ServalDInterfaceException("missing JSON column: read_offset");
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);
}
}
public MeshMSConversation nextConversation() 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);
Object[] row = new Object[headers.size()];
json.consumeArray(row);
int _id = JSONTokeniser.narrow(row[columnIndex__id], Integer.class);
SubscriberId my_sid;
try {
my_sid = new SubscriberId(JSONTokeniser.narrow(row[columnIndex_my_sid], String.class));
}
catch (SubscriberId.InvalidHexException e) {
throw new ServalDInterfaceException("invalid column value: my_sid", e);
}
SubscriberId their_sid;
try {
their_sid = new SubscriberId(JSONTokeniser.narrow(row[columnIndex_their_sid], String.class));
}
catch (SubscriberId.InvalidHexException e) {
throw new ServalDInterfaceException("invalid column value: their_sid", e);
}
boolean is_read = JSONTokeniser.narrow(row[columnIndex_read], Boolean.class);
int last_message = JSONTokeniser.narrow(row[columnIndex_last_message], Integer.class);
int read_offset = JSONTokeniser.narrow(row[columnIndex_read_offset], Integer.class);
return new MeshMSConversation(rowCount++, _id, my_sid, their_sid, is_read, last_message, read_offset);
}
catch (JSONInputException e) {
throw new ServalDInterfaceException(e);
}
}
public void close() throws IOException
{
httpConnection = null;
if (json != null) {
json.close();
json = null;
}
headers = null;
}
}

View File

@ -0,0 +1,40 @@
/**
* Copyright (C) 2014 Serval Project Inc.
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.servaldna.meshms;
import java.net.URL;
/**
* Thrown when a MeshMS API encounters an exceptional condition. This exception is subclassed for
* specific causes.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
public abstract class MeshMSException extends Exception
{
public final URL url;
public MeshMSException(String message, URL url) {
super(message + "; " + url);
this.url = url;
}
}

View File

@ -0,0 +1,78 @@
/**
* Copyright (C) 2014 Serval Project Inc.
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.servaldna.meshms;
import org.servalproject.servaldna.SubscriberId;
import org.servalproject.servaldna.ServalDInterfaceException;
public class MeshMSMessage {
public enum Type {
MESSAGE_SENT,
MESSAGE_RECEIVED,
ACK_RECEIVED
};
public final int _rowNumber;
public final Type type;
public final SubscriberId mySid;
public final SubscriberId theirSid;
public final int offset;
public final String token;
public final String text;
public final boolean isDelivered;
public final boolean isRead;
public final Integer ackOffset;
protected MeshMSMessage(int rowNumber,
Type type,
SubscriberId my_sid,
SubscriberId their_sid,
int offset,
String token,
String text,
boolean delivered,
boolean read,
Integer ack_offset) throws ServalDInterfaceException
{
if (my_sid == null)
throw new ServalDInterfaceException("my_sid is null");
if (their_sid == null)
throw new ServalDInterfaceException("their_sid is null");
if (type != Type.ACK_RECEIVED && text == null)
throw new ServalDInterfaceException("text is null");
if (token == null)
throw new ServalDInterfaceException("token is null");
if (type == Type.ACK_RECEIVED && ack_offset == null)
throw new ServalDInterfaceException("ack_offset is null");
this._rowNumber = rowNumber;
this.type = type;
this.mySid = my_sid;
this.theirSid = their_sid;
this.offset = offset;
this.token = token;
this.text = text;
this.isDelivered = delivered;
this.isRead = read;
this.ackOffset = ack_offset;
}
}

View File

@ -0,0 +1,220 @@
/**
* Copyright (C) 2014 Serval Project Inc.
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.servaldna.meshms;
import java.io.IOException;
import java.util.Vector;
import java.net.HttpURLConnection;
import org.servalproject.servaldna.ServalDHttpConnectionFactory;
import org.servalproject.servaldna.ServalDInterfaceException;
import org.servalproject.servaldna.SubscriberId;
import org.servalproject.json.JSONTokeniser;
import org.servalproject.json.JSONInputException;
public class MeshMSMessageList {
private ServalDHttpConnectionFactory httpConnector;
private SubscriberId my_sid;
private SubscriberId their_sid;
private HttpURLConnection httpConnection;
private JSONTokeniser json;
private int readOffset;
private int latestAckOffset;
private Vector<String> headers;
private int columnIndex_type;
private int columnIndex_my_sid;
private int columnIndex_their_sid;
private int columnIndex_offset;
private int columnIndex_token;
private int columnIndex_text;
private int columnIndex_delivered;
private int columnIndex_read;
private int columnIndex_ack_offset;
private int rowCount;
public MeshMSMessageList(ServalDHttpConnectionFactory connector, SubscriberId my_sid, SubscriberId their_sid)
{
this.httpConnector = connector;
this.my_sid = my_sid;
this.their_sid = their_sid;
}
public boolean isConnected()
{
return this.json != null;
}
public void connect() throws MeshMSException, ServalDInterfaceException, IOException
{
assert json == null;
try {
columnIndex_type = -1;
columnIndex_my_sid = -1;
columnIndex_their_sid = -1;
columnIndex_offset = -1;
columnIndex_token = -1;
columnIndex_text = -1;
columnIndex_delivered = -1;
columnIndex_read = -1;
columnIndex_ack_offset = -1;
rowCount = 0;
httpConnection = httpConnector.newServalDHttpConnection("/restful/meshms/" + my_sid.toHex() + "/" + their_sid.toHex() + "/messagelist.json");
json = MeshMSCommon.connectMeshMSRestful(httpConnection);
json.consume(JSONTokeniser.Token.START_OBJECT);
json.consume("read_offset");
json.consume(JSONTokeniser.Token.COLON);
readOffset = json.consume(Integer.class);
json.consume(JSONTokeniser.Token.COMMA);
json.consume("latest_ack_offset");
json.consume(JSONTokeniser.Token.COLON);
latestAckOffset = json.consume(Integer.class);
json.consume(JSONTokeniser.Token.COMMA);
json.consume("header");
json.consume(JSONTokeniser.Token.COLON);
headers = new Vector<String>();
json.consumeArray(headers, String.class);
if (headers.size() < 1)
throw new ServalDInterfaceException("empty JSON headers array");
for (int i = 0; i < headers.size(); ++i) {
String header = headers.get(i);
if (header.equals("type"))
columnIndex_type = i;
else if (header.equals("my_sid"))
columnIndex_my_sid = i;
else if (header.equals("their_sid"))
columnIndex_their_sid = i;
else if (header.equals("offset"))
columnIndex_offset = i;
else if (header.equals("token"))
columnIndex_token = i;
else if (header.equals("text"))
columnIndex_text = i;
else if (header.equals("delivered"))
columnIndex_delivered = i;
else if (header.equals("read"))
columnIndex_read = i;
else if (header.equals("ack_offset"))
columnIndex_ack_offset = i;
}
if (columnIndex_type == -1)
throw new ServalDInterfaceException("missing JSON column: type");
if (columnIndex_my_sid == -1)
throw new ServalDInterfaceException("missing JSON column: my_sid");
if (columnIndex_their_sid == -1)
throw new ServalDInterfaceException("missing JSON column: their_sid");
if (columnIndex_offset == -1)
throw new ServalDInterfaceException("missing JSON column: offset");
if (columnIndex_token == -1)
throw new ServalDInterfaceException("missing JSON column: token");
if (columnIndex_text == -1)
throw new ServalDInterfaceException("missing JSON column: text");
if (columnIndex_delivered == -1)
throw new ServalDInterfaceException("missing JSON column: delivered");
if (columnIndex_read == -1)
throw new ServalDInterfaceException("missing JSON column: read");
if (columnIndex_ack_offset == -1)
throw new ServalDInterfaceException("missing JSON column: ack_offset");
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);
}
}
public int getReadOffset()
{
assert json != null;
return readOffset;
}
public int getLatestAckOffset()
{
assert json != null;
return latestAckOffset;
}
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);
Object[] row = new Object[headers.size()];
json.consumeArray(row, JSONTokeniser.Narrow.ALLOW_NULL);
String typesym = JSONTokeniser.narrow(row[columnIndex_type], String.class);
SubscriberId my_sid;
try {
my_sid = new SubscriberId(JSONTokeniser.narrow(row[columnIndex_my_sid], String.class));
}
catch (SubscriberId.InvalidHexException e) {
throw new ServalDInterfaceException("invalid column value: my_sid", e);
}
SubscriberId their_sid;
try {
their_sid = new SubscriberId(JSONTokeniser.narrow(row[columnIndex_their_sid], String.class));
}
catch (SubscriberId.InvalidHexException e) {
throw new ServalDInterfaceException("invalid column value: their_sid", e);
}
int offset = JSONTokeniser.narrow(row[columnIndex_offset], Integer.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);
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, my_sid, their_sid, offset, token, text, is_delivered, is_read, ack_offset);
}
catch (JSONInputException e) {
throw new ServalDInterfaceException(e);
}
}
public void close() throws IOException
{
httpConnection = null;
if (json != null) {
json.close();
json = null;
}
headers = null;
}
}

View File

@ -0,0 +1,39 @@
/**
* Copyright (C) 2014 Serval Project Inc.
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.servaldna.meshms;
import java.net.URL;
/**
* Thrown when a MeshMS API method is used to request a message for an unknown identity.
* This is not an error in the Serval DNA interface, so it is not a subclass of
* ServalDInterfaceException, so the programmer must explicitly deal with it instead of just
* absorbing it as an interface malfunction.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
public class MeshMSProtocolFaultException extends MeshMSException
{
public MeshMSProtocolFaultException(URL url) {
super("MeshMS protocol fault", url);
}
}

View File

@ -0,0 +1,39 @@
/**
* Copyright (C) 2014 Serval Project Inc.
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.servaldna.meshms;
import java.net.URL;
/**
* Thrown when a MeshMS API method is used to request a message for an unknown identity.
* This is not an error in the Serval DNA interface, so it is not a subclass of
* ServalDInterfaceException, so the programmer must explicitly deal with it instead of just
* absorbing it as an interface malfunction.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
public class MeshMSUnknownIdentityException extends MeshMSException
{
public MeshMSUnknownIdentityException(URL url) {
super("unknown identity", url);
}
}

View File

@ -0,0 +1,114 @@
/**
* Copyright (C) 2014 Serval Project Inc.
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.test;
import java.lang.System;
import java.io.IOException;
import org.servalproject.servaldna.SubscriberId;
import org.servalproject.servaldna.ServalDClient;
import org.servalproject.servaldna.ServalDInterfaceException;
import org.servalproject.servaldna.meshms.MeshMSConversationList;
import org.servalproject.servaldna.meshms.MeshMSConversation;
import org.servalproject.servaldna.meshms.MeshMSMessageList;
import org.servalproject.servaldna.meshms.MeshMSMessage;
import org.servalproject.servaldna.meshms.MeshMSException;
public class Meshms {
static void meshms_list_conversations(SubscriberId sid) throws ServalDInterfaceException, IOException, InterruptedException
{
ServalDClient client = ServalDClient.newServalDClient();
MeshMSConversationList list = null;
try {
list = client.meshmsListConversations(sid);
MeshMSConversation conv;
while ((conv = list.nextConversation()) != null) {
System.out.println(
"_id=" + conv._id +
", my_sid=" + conv.mySid +
", their_sid=" + conv.theirSid +
", read=" + conv.isRead +
", last_message=" + conv.lastMessageOffset +
", read_offset=" + conv.readOffset
);
}
}
catch (MeshMSException e) {
System.out.println(e.toString());
}
finally {
if (list != null)
list.close();
}
System.exit(0);
}
static void meshms_list_messages(SubscriberId sid1, SubscriberId sid2) throws ServalDInterfaceException, IOException, InterruptedException
{
ServalDClient client = ServalDClient.newServalDClient();
MeshMSMessageList list = null;
try {
list = client.meshmsListMessages(sid1, sid2);
System.out.println("read_offset=" + list.getReadOffset());
System.out.println("latest_ack_offset=" + list.getLatestAckOffset());
MeshMSMessage msg;
while ((msg = list.nextMessage()) != null) {
System.out.println("type=" + msg.type
+ ", my_sid=" + msg.mySid
+ ", their_sid=" + msg.theirSid
+ ", offset=" + msg.offset
+ ", token=" + msg.token
+ ", text=" + (msg.text == null ? null : msg.text.replace('\n', '.').replace(' ', '.'))
+ ", delivered=" + msg.isDelivered
+ ", read=" + msg.isRead
+ ", ack_offset=" + msg.ackOffset
);
}
}
catch (MeshMSException e) {
System.out.println(e.toString());
}
finally {
if (list != null)
list.close();
}
System.exit(0);
}
public static void main(String... args)
{
if (args.length < 1)
return;
String methodName = args[0];
try {
if (methodName.equals("meshms-list-conversations"))
meshms_list_conversations(new SubscriberId(args[1]));
else if (methodName.equals("meshms-list-messages"))
meshms_list_messages(new SubscriberId(args[1]), new SubscriberId(args[2]));
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
System.err.println("No such command: " + methodName);
System.exit(1);
}
}

View File

@ -1,5 +1,5 @@
# Common definitions for all test suites.
# Copyright 2012 The Serval Project, Inc.
# Copyright 2012 Serval Project Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -321,14 +321,6 @@ foreach_instance_with_pidfile() {
foreach_instance "${instances[@]}" "$@"
}
# Utility function for setting up servald JNI fixtures:
# - check that libserval.so is present
# - set LD_LIBRARY_PATH so that libserval.so can be found
setup_servald_so() {
assert [ -r "$servald_build_root/libserval.so" ]
export LD_LIBRARY_PATH="$servald_build_root"
}
# Utility function for setting up a fixture with a servald server process:
# - start a servald server process
# - assert that the pidfile is created and correct

58
testdefs_java.sh Normal file
View File

@ -0,0 +1,58 @@
# Definitions for test suites using Java.
# Copyright 2014 Serval Project Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
source "${0%/*}/../testconfig.sh"
# Utility function for setting up servald JNI fixtures:
# - check that libserval.so is present
# - set LD_LIBRARY_PATH so that libserval.so can be found
setup_servald_so() {
assert [ -r "$servald_build_root/libserval.so" ]
export LD_LIBRARY_PATH="$servald_build_root"
}
compile_java_classes() {
assert --message='Java compiler was detected by ./configure' [ "$JAVAC" ]
mkdir classes
assert find "$servald_source_root"/java/ -name *.java | xargs $JAVAC -Xlint:unchecked -d classes
assert [ -r classes/org/servalproject/servaldna/ServalDCommand.class ]
assert [ -r classes/org/servalproject/servaldna/IJniResults.class ]
assert [ -r classes/org/servalproject/test/ServalDTests.class ]
}
_executeJava() {
local func="${1?}"
shift
local opts=()
while [ $# -ne 0 ]; do
case "$1" in
--) shift; break;;
--*) opts+=("$1"); shift;;
*) break;;
esac
done
"$func" "${opts[@]}" --core-backtrace java "-Djava.library.path=$LD_LIBRARY_PATH" -classpath "$PWD/classes" "$@"
}
executeJava() {
_executeJava execute "$@"
}
executeJavaOk() {
_executeJava executeOk "$@"
}

71
testdefs_meshms.sh Normal file
View File

@ -0,0 +1,71 @@
# Common definitions for MeshMS test suites.
# Copyright 2014 Serval Project Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Create a file that contains no blank lines.
meshms_create_message() {
create_file --label="$1" - $2 | sed -e '/^$/d'
}
# Add a sequence of messages of varying sizes up to 1 KiB.
meshms_add_messages() {
local sid1="${1?}"
local sid2="${2?}"
local symbols="${3?}"
shift 3
local texts=("$@")
local sent_since_ack=0
local i n size msize
local size=0
for ((i = 0; i < ${#symbols}; ++i)); do
local sym="${symbols:$i:1}"
let size+=379
let msize=size%1021
let n=NMESSAGE++
local text="${texts[$i]}"
case $sym in
'>'|'<')
if [ -n "$text" ]; then
TEXT[$n]="$text"
else
TEXT[$n]="$(meshms_create_message "message$n" $msize)"
fi
;;
esac
case $sym in
'>')
MESSAGE[$n]=">"
executeOk_servald meshms send message $sid1 $sid2 "${TEXT[$n]}"
let ++sent_since_ack
let ++NSENT
;;
'<')
MESSAGE[$n]="<"
executeOk_servald meshms send message $sid2 $sid1 "${TEXT[$n]}"
let ++NRECV
;;
'A')
MESSAGE[$n]=ACK
[ $i -ne 0 -a $sent_since_ack -eq 0 ] && error "two ACKs in a row (at position $i)"
executeOk_servald meshms list messages $sid2 $sid1
let ++NACK
;;
*)
error "invalid message symbol '$sym' (at position $i)"
;;
esac
done
}

View File

@ -1,5 +1,5 @@
# Common definitions for rhizome test suites.
# Copyright 2012 The Serval Project, Inc.
# Common definitions for Rhizome test suites.
# Copyright 2012 Serval Project Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License

View File

@ -2099,19 +2099,37 @@ tfw_multicolumn() {
}
# Create a file with the given size (default 0).
# Usage: create_file [--append] <path> [<size>]
# where: <size> is of the form Nu
# Usage: create_file [--append] [create_file opts] [--] <path> [<size>]
# where: if <path> is - then writes to standard output
# <size> is of the form Nu
# N is decimal integer
# u is one of kKmMgG (k=10^3, K=2^10, m=10^6, M=2^20, g=10^9, G=2^30)
create_file() {
local args=("$@")
case "$1" in
--append) shift;;
*) rm -f "$1";;
esac
local path="$1"
local opt_append=false
local opt_label=
local opts=()
while [ $# -ne 0 ]; do
case "$1" in
--) shift; break;;
--append) opt_append=true; shift;;
--label=*) opt_label="${1#*=}"; shift;;
--*) opts+=("$1"); shift;;
*) break;;
esac
done
local path="${1?}"
local size="$2"
tfw_createfile --label="$path" ${size:+--size=$size} >>"$path" || error "failed command: create_file ${args[*]}"
case "$path" in
-)
tfw_createfile ${opt_label:+--label="$opt_label"} "${opts[@]}" ${size:+--size=$size}
;;
*)
[ -z "$opt_label" ] && opt_label="$path"
tfw_createfile ${opt_label:+--label="$opt_label"} "${opts[@]}" ${size:+--size=$size} >>"$path"
;;
esac
[ $? -eq 0 ] || error "failed command: create_file ${args[*]}"
}
# Add quotations to the given arguments to allow them to be expanded intact

View File

@ -2,7 +2,7 @@
# Tests for Serval DNA JNI entry points.
#
# Copyright 2012 Serval Project, Inc.
# Copyright 2012-2014 Serval Project Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -20,7 +20,7 @@
source "${0%/*}/../testframework.sh"
source "${0%/*}/../testdefs.sh"
source "${0%/*}/../testconfig.sh"
source "${0%/*}/../testdefs_java.sh"
setup() {
setup_servald
@ -32,15 +32,6 @@ setup() {
setup_servald_so
}
compile_java_classes() {
assert --message='Java compiler was detected by ./configure' [ "$JAVAC" ]
mkdir classes
assert find "$servald_source_root"/java/ -name *.java | xargs $JAVAC -Xlint:unchecked -d classes
assert [ -r classes/org/servalproject/servaldna/ServalDCommand.class ]
assert [ -r classes/org/servalproject/servaldna/IJniResults.class ]
assert [ -r classes/org/servalproject/test/ServalDTests.class ]
}
# Make sure that the normal echo command-line works, without JNI.
assert_echo_works() {
executeOk $servald echo -e 'Hello,\ttab' 'world\0!'
@ -49,7 +40,7 @@ assert_echo_works() {
doc_Echo="Serval JNI echo Hello world"
test_Echo() {
executeOk java "-Djava.library.path=$LD_LIBRARY_PATH" -classpath "$PWD/classes" org.servalproject.test.ServalDTests 'echo' '-e' 'Hello,\ttab' 'world\0!'
executeJavaOk org.servalproject.test.ServalDTests 'echo' '-e' 'Hello,\ttab' 'world\0!'
assertStdoutIs -e 'Hello,\ttab world\0! \n'
}
@ -66,14 +57,14 @@ test_Delim() {
doc_Repeat="Serval JNI repeated calls in same process"
test_Repeat() {
executeOk --core-backtrace java "-Djava.library.path=$LD_LIBRARY_PATH" -classpath "$PWD/classes" org.servalproject.test.ServalDTests repeat 50 'echo' 'Hello,' 'world!'
executeJavaOk org.servalproject.test.ServalDTests repeat 50 'echo' 'Hello,' 'world!'
assertStdoutLineCount '==' 50
assertStdoutGrep --matches=50 '^Hello, world! $'
}
doc_NullArg="Serval JNI null arguments throw exception"
test_NullArg() {
execute --core-backtrace java "-Djava.library.path=$LD_LIBRARY_PATH" -classpath "$PWD/classes" org.servalproject.test.ServalDTests 'echo' '(null)'
executeJava org.servalproject.test.ServalDTests 'echo' '(null)'
tfw_cat --stdout --stderr
assertExitStatus '!=' 0
assertStderrGrep 'NullPointerException: null element in argv'
@ -81,13 +72,11 @@ test_NullArg() {
doc_help="Serval JNI returns help text"
test_help() {
execute --core-backtrace java "-Djava.library.path=$LD_LIBRARY_PATH" -classpath "$PWD/classes" org.servalproject.test.ServalDTests 'help'
tfw_cat --stdout --stderr
assertExitStatus '==' 0
executeJavaOk org.servalproject.test.ServalDTests 'help'
assertStdoutGrep 'Serval DNA version '
}
doc_PeerList="Get peer details via JNI"
doc_PeerList="Serval JNI get peer details"
setup_PeerList() {
configure_servald_server() {
add_servald_interface
@ -98,7 +87,7 @@ setup_PeerList() {
set_instance +A
}
test_PeerList() {
execute --core-backtrace java "-Djava.library.path=$LD_LIBRARY_PATH" -classpath "$PWD/classes" org.servalproject.test.CommandLine 'peers'
executeJavaOk org.servalproject.test.CommandLine 'peers'
assertStdoutGrep "$SIDB"
tfw_cat --stdout
}
@ -109,7 +98,7 @@ teardown_PeerList() {
report_all_servald_servers
}
doc_DnaLookup="DNA Lookup via JNI MDP API"
doc_DnaLookup="Serval JNI DNA Lookup"
setup_DnaLookup() {
configure_servald_server() {
add_servald_interface
@ -123,9 +112,9 @@ setup_DnaLookup() {
set_instance +A
}
test_DnaLookup() {
execute --timeout=10 --core-backtrace java "-Djava.library.path=$LD_LIBRARY_PATH" -classpath "$PWD/classes" org.servalproject.test.CommandLine 'lookup'
assertStdoutGrep "$SIDB"
executeJavaOk --timeout=10 org.servalproject.test.CommandLine 'lookup'
tfw_cat --stdout --stderr
assertStdoutGrep "$SIDB"
}
teardown_DnaLookup() {
stop_all_servald_servers
@ -134,7 +123,7 @@ teardown_DnaLookup() {
report_all_servald_servers
}
doc_serviceDiscovery="Discover network services by name"
doc_serviceDiscovery="Serval JNI discover network services by name"
listen_service() {
executeOk_servald --timeout=20 msp listen --service=test_name 512 <<EOF
Hi!
@ -158,9 +147,9 @@ setup_serviceDiscovery() {
set_instance +A
}
test_serviceDiscovery() {
execute --timeout=10 --core-backtrace java "-Djava.library.path=$LD_LIBRARY_PATH" -classpath "$PWD/classes" org.servalproject.test.CommandLine 'service' 'test_name.*'
executeJavaOk --timeout=10 org.servalproject.test.CommandLine 'service' 'test_name.*'
assertStdoutGrep "$SIDB"
assertStdoutGrep "test_name.msp.port=512"
assertStdoutGrep "\<test_name\.msp\.port=512\>"
tfw_cat --stdout --stderr
}
teardown_serviceDiscovery() {
@ -169,4 +158,5 @@ teardown_serviceDiscovery() {
assert_no_servald_processes
report_all_servald_servers
}
runTests "$@"

155
tests/meshmsjava Executable file
View File

@ -0,0 +1,155 @@
#!/bin/bash
# Tests for MeshMS Java API.
#
# Copyright 2014 Serval Project Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
source "${0%/*}/../testframework.sh"
source "${0%/*}/../testdefs.sh"
source "${0%/*}/../testdefs_java.sh"
source "${0%/*}/../testdefs_meshms.sh"
setup() {
setup_servald
setup_servald_so
compile_java_classes
set_instance +A
executeOk_servald config \
set log.console.level debug \
set debug.httpd on
create_identities 4
configure_servald_server() {
add_servald_interface
executeOk_servald config \
set rhizome.api.restful.users.joe.password bloggs
}
start_servald_server
}
teardown() {
stop_all_servald_servers
kill_all_servald_processes
assert_no_servald_processes
report_all_servald_servers
}
doc_MeshmsListConversations="Java API list MeshMS conversations"
setup_MeshmsListConversations() {
setup
# create 3 threads, with all permutations of incoming and outgoing messages
executeOk_servald meshms send message $SIDA1 $SIDA2 "Message One"
executeOk_servald meshms send message $SIDA3 $SIDA1 "Message Two"
executeOk_servald meshms send message $SIDA1 $SIDA4 "Message Three"
executeOk_servald meshms send message $SIDA4 $SIDA1 "Message Four"
}
test_MeshmsListConversations() {
executeJavaOk org.servalproject.test.Meshms meshms-list-conversations $SIDA1
tfw_cat --stderr
assertStdoutLineCount '==' 3
assertStdoutGrep "my_sid=$SIDA1, their_sid=$SIDA2, read=true, last_message=0, read_offset=0"
assertStdoutGrep "my_sid=$SIDA1, their_sid=$SIDA3, read=false, last_message=14, read_offset=0"
assertStdoutGrep "my_sid=$SIDA1, their_sid=$SIDA4, read=false, last_message=18, read_offset=0"
executeOk_servald meshms read messages $SIDA1
executeJavaOk org.servalproject.test.Meshms meshms-list-conversations $SIDA1
assertStdoutLineCount '==' 3
assertStdoutGrep "my_sid=$SIDA1, their_sid=$SIDA2, read=true, last_message=0, read_offset=0"
assertStdoutGrep "my_sid=$SIDA1, their_sid=$SIDA3, read=true, last_message=14, read_offset=14"
assertStdoutGrep "my_sid=$SIDA1, their_sid=$SIDA4, read=true, last_message=18, read_offset=18"
}
doc_MeshmsListMessages="Java API list MeshMS messages in one conversation"
setup_MeshmsListMessages() {
setup
meshms_add_messages $SIDA1 $SIDA2 '><>>A>A<>><><><>>>A>A><<<<A<>><>>A<<>'
let NROWS=NSENT+NRECV+(NACK?1:0)
executeOk_servald meshms list messages $SIDA1 $SIDA2
delivered_offset=$(sed -n -e '/^[0-9]\+:[0-9]\+:ACK:delivered$/{n;s/^[0-9]\+:\([0-9]\+\):>:.*/\1/p;q}' "$TFWSTDOUT")
[ -z "$delivered_offset" ] && delivered_offset=0
read_offset=$(sed -n -e 's/^[0-9]\+:\([0-9]\+\):MARK:read$/\1/p' "$TFWSTDOUT")
[ -z "$read_offset" ] && read_offset=0
tfw_log delivered_offset="$delivered_offset" read_offset="$read_offset"
}
test_MeshmsListMessages() {
executeJavaOk org.servalproject.test.Meshms meshms-list-messages $SIDA1 $SIDA2
assertStdoutLineCount '==' $(($NROWS + 2))
assertStdoutIs --line=1 -e "read_offset=$read_offset\n"
assertStdoutIs --line=2 -e "latest_ack_offset=$delivered_offset\n"
seen_ack=false
let lnum=3
for ((j = NMESSAGE-1; j >= 0; --j)); do
case ${MESSAGE[$j]} in
'ACK') $seen_ack && continue
esac
assertStdoutGrep --line=$lnum 'token=[-_A-Za-z0-9=]\+,'
assertStdoutGrep --line=$lnum "my_sid=$SIDA1,"
assertStdoutGrep --line=$lnum "their_sid=$SIDA2,"
text="$(sed -n -e $lnum's/.*\<text=\([^ ]*\), .*/\1/p' "$TFWSTDOUT")"
offset="$(sed -n -e $lnum's/.*\<offset=\([0-9]\+\).*/\1/p' "$TFWSTDOUT")"
is_delivered="$(sed -n -e $lnum's/.*\<delivered=\(true\|false\).*/\1/p' "$TFWSTDOUT")"
is_read="$(sed -n -e $lnum's/.*\<read=\(true\|false\).*/\1/p' "$TFWSTDOUT")"
ack_offset="$(sed -n -e $lnum's/.*\<ack_offset=\(null\|[0-9]\+\).*/\1/p' "$TFWSTDOUT")"
tfw_log text="$text" offset="$offset" is_delivered="$is_delivered" is_read="$is_read" ack_offset="$ack_offset"
case ${MESSAGE[$j]} in
'>'|'<')
echo -n "${TEXT[$j]}" | tr ' \n' . >text_fixture
echo -n "$text" >text_list
assert --dump-on-fail=text_fixture --dump-on-fail=text_list cmp text_fixture text_list
assert [ "$ack_offset" = null ]
;;
esac
case ${MESSAGE[$j]} in
'>')
assertStdoutGrep --line=$lnum 'type=MESSAGE_SENT,'
if [ "$offset" -le "$delivered_offset" ]; then
assert [ "$is_delivered" = true ]
else
assert [ "$is_delivered" = false ]
fi
let ++lnum
;;
'<')
assertStdoutGrep --line=$lnum 'type=MESSAGE_RECEIVED,'
if [ "$offset" -le "$read_offset" ]; then
assert [ "$is_read" = true ]
else
assert [ "$is_read" = false ]
fi
let ++lnum
;;
'ACK')
assertStdoutGrep --line=$lnum 'type=ACK_RECEIVED,'
assert [ "$text" = null ]
assert [ "$ack_offset" = "$delivered_offset" ]
seen_ack=true
let ++lnum
;;
esac
done
}
doc_MeshmsListMessagesNoIdentity="Java API list MeshMS messages from unknown identity"
setup_MeshmsListMessagesNoIdentity() {
setup
SIDX=0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
}
test_MeshmsListMessagesNoIdentity() {
executeJavaOk org.servalproject.test.Meshms meshms-list-messages $SIDX $SIDA2
assertStdoutGrep 'MeshMSUnknownIdentityException'
tfw_cat --stdout --stderr
}
runTests "$@"

View File

@ -21,6 +21,7 @@
source "${0%/*}/../testframework.sh"
source "${0%/*}/../testdefs.sh"
source "${0%/*}/../testdefs_rhizome.sh"
source "${0%/*}/../testdefs_meshms.sh"
shopt -s extglob
@ -47,6 +48,23 @@ assertJqCmp() {
assert --dump-on-fail="$TFWTMP/jqcmp.tmp" --dump-on-fail="$file" "${opts[@]}" cmp "$TFWTMP/jqcmp.tmp" "$file"
}
assertJqIs() {
local opts=()
while [ $# -gt 0 ]; do
case "$1" in
--) shift; break;;
--*) opts+=("$1"); shift;;
*) break;;
esac
done
[ $# -eq 3 ] || error "invalid arguments"
local json="$1"
local jqscript="$2"
local text="$3"
local jqout="$(jq --raw-output "$jqscript" "$json")"
assert "${opts[@]}" [ "$jqout" = "$text" ]
}
assertJqGrep() {
local opts=()
while [ $# -gt 0 ]; do
@ -989,66 +1007,11 @@ test_MeshmsListConversations() {
])"
}
# Create a file that contains no blank lines.
create_message_file() {
create_file "$1" $2
sed -i -e '/^$/d' "$1"
}
# Add a sequence of messages of varying sizes up to 1 KiB.
add_messages() {
local symbols="$1"
shift
local texts=("$@")
local sent_since_ack=0
local i n size msize
local size=0
for ((i = 0; i < ${#symbols}; ++i)); do
local sym="${symbols:$i:1}"
let size+=379
let msize=size%1021
let n=NMESSAGE++
local text="${texts[$i]}"
case $sym in
'>'|'<')
if [ -n "$text" ]; then
echo "$text" >text$n
else
create_message_file text$n $msize
text="$(<text$n)"
fi
;;
esac
case $sym in
'>')
MESSAGE[$n]=">"
executeOk_servald meshms send message $SIDA1 $SIDA2 "$text"
let ++sent_since_ack
let ++NSENT
;;
'<')
MESSAGE[$n]="<"
executeOk_servald meshms send message $SIDA2 $SIDA1 "$text"
let ++NRECV
;;
'A')
MESSAGE[$n]=ACK
[ $i -ne 0 -a $sent_since_ack -eq 0 ] && error "two ACKs in a row (at position $i)"
executeOk_servald meshms list messages $SIDA2 $SIDA1
let ++NACK
;;
*)
error "invalid message symbol '$sym' (at position $i)"
;;
esac
done
}
doc_MeshmsListMessages="HTTP RESTful list MeshMS messages in one conversation as JSON"
setup_MeshmsListMessages() {
IDENTITY_COUNT=2
setup
add_messages '><>>A>A<>><><><>>>A>A><<<<A<>><>>A<<>'
meshms_add_messages $SIDA1 $SIDA2 '><>>A>A<>><><><>>>A>A><<<<A<>><>>A<<>'
let NROWS=NSENT+NRECV+(NACK?1:0)
executeOk_servald meshms list messages $SIDA1 $SIDA2
delivered_offset=$(sed -n -e '/^[0-9]\+:[0-9]\+:ACK:delivered$/{n;s/^[0-9]\+:\([0-9]\+\):>:.*/\1/p;q}' "$TFWSTDOUT")
@ -1080,13 +1043,13 @@ test_MeshmsListMessages() {
case ${MESSAGE[$j]} in
'>')
assertJq messages.json '.['$i'].type == ">"'
assertJqCmp messages.json '.['$i'].text' text$j
assertJqIs messages.json '.['$i'].text' "${TEXT[$j]}"
assertJq messages.json '.['$i'].delivered == (.['$i'].offset <= '$delivered_offset')'
let ++i
;;
'<')
assertJq messages.json '.['$i'].type == "<"'
assertJqCmp messages.json '.['$i'].text' text$j
assertJqIs messages.json '.['$i'].text' "${TEXT[$j]}"
assertJq messages.json '.['$i'].read == (.['$i'].offset <= '$read_offset')'
let ++i
;;
@ -1129,7 +1092,7 @@ setup_MeshmsListMessagesNewSince() {
set rhizome.api.restful.newsince_poll_ms 500
}
setup
add_messages '><>>A>A<>><><><>>>A>A><<<<A<>><>>A<<>'
meshms_add_messages $SIDA1 $SIDA2 '><>>A>A<>><><><>>>A>A><<<<A<>><>>A<<>'
let NROWS=NSENT+NRECV+(NACK?1:0)
executeOk curl \
--silent --fail --show-error \
@ -1181,7 +1144,7 @@ setup_MeshmsListMessagesNewSinceArrival() {
set rhizome.api.restful.newsince_poll_ms 500
}
setup
add_messages '><>A>'
meshms_add_messages $SIDA1 $SIDA2 '><>A>'
let NROWS=NSENT+NRECV+(NACK?1:0)
executeOk curl \
--silent --fail --show-error \
@ -1206,7 +1169,7 @@ test_MeshmsListMessagesNewSinceArrival() {
done
wait_until [ -e newsince1.json -a -e newsince2.json -a -e newsince3.json ]
for message in '>Rumplestiltskin' 'A' '<Howdydoody' '>Eulenspiegel'; do
add_messages "${message:0:1}" "${message:1}"
meshms_add_messages $SIDA1 $SIDA2 "${message:0:1}" "${message:1}"
wait_until --timeout=60 grepall "${message:1}" newsince{1,2,3}.json
done
fork_terminate_all