mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-04-14 22:26:44 +00:00
Merge NAF4 MeshMS Java API into development
Currently implements "list conversations" and "list messages"
This commit is contained in:
commit
b14c58bdf6
@ -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);
|
||||
|
70
java/org/servalproject/codec/Base64.java
Normal file
70
java/org/servalproject/codec/Base64.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
43
java/org/servalproject/json/JSONInputException.java
Normal file
43
java/org/servalproject/json/JSONInputException.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
459
java/org/servalproject/json/JSONTokeniser.java
Normal file
459
java/org/servalproject/json/JSONTokeniser.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
131
java/org/servalproject/servaldna/ServalDClient.java
Normal file
131
java/org/servalproject/servaldna/ServalDClient.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
72
java/org/servalproject/servaldna/meshms/MeshMSCommon.java
Normal file
72
java/org/servalproject/servaldna/meshms/MeshMSCommon.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
40
java/org/servalproject/servaldna/meshms/MeshMSException.java
Normal file
40
java/org/servalproject/servaldna/meshms/MeshMSException.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
78
java/org/servalproject/servaldna/meshms/MeshMSMessage.java
Normal file
78
java/org/servalproject/servaldna/meshms/MeshMSMessage.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
220
java/org/servalproject/servaldna/meshms/MeshMSMessageList.java
Normal file
220
java/org/servalproject/servaldna/meshms/MeshMSMessageList.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
114
java/org/servalproject/test/Meshms.java
Normal file
114
java/org/servalproject/test/Meshms.java
Normal 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);
|
||||
}
|
||||
}
|
10
testdefs.sh
10
testdefs.sh
@ -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
58
testdefs_java.sh
Normal 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
71
testdefs_meshms.sh
Normal 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
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
40
tests/jni
40
tests/jni
@ -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
155
tests/meshmsjava
Executable 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 "$@"
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user