mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-01-29 15:43:56 +00:00
MeshMS Java API: list conversations
This commit is contained in:
parent
d879189299
commit
9cbd7c365c
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();
|
||||
}
|
||||
|
||||
}
|
122
java/org/servalproject/servaldna/ServalDClient.java
Normal file
122
java/org/servalproject/servaldna/ServalDClient.java
Normal file
@ -0,0 +1,122 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
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
|
||||
{
|
||||
MeshMSConversationList list = new MeshMSConversationList(this, sid);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -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,438 @@
|
||||
/**
|
||||
* 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.lang.StringBuilder;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.PushbackReader;
|
||||
import java.util.Collection;
|
||||
import java.util.Vector;
|
||||
import java.net.HttpURLConnection;
|
||||
import org.servalproject.servaldna.ServalDHttpConnectionFactory;
|
||||
import org.servalproject.servaldna.ServalDInterfaceException;
|
||||
import org.servalproject.servaldna.SubscriberId;
|
||||
|
||||
public class MeshMSConversationList {
|
||||
|
||||
private ServalDHttpConnectionFactory httpConnector;
|
||||
private SubscriberId sid;
|
||||
private HttpURLConnection httpConnection;
|
||||
private PushbackReader reader;
|
||||
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 void connect() throws ServalDInterfaceException, IOException
|
||||
{
|
||||
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");
|
||||
httpConnection.connect();
|
||||
reader = new PushbackReader(new InputStreamReader(httpConnection.getInputStream(), "US-ASCII"));
|
||||
consume(reader, JsonToken.START_OBJECT);
|
||||
consume(reader, "header");
|
||||
consume(reader, JsonToken.COLON);
|
||||
headers = new Vector<String>();
|
||||
consumeArray(reader, 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");
|
||||
consume(reader, JsonToken.COMMA);
|
||||
consume(reader, "rows");
|
||||
consume(reader, JsonToken.COLON);
|
||||
consume(reader, JsonToken.START_ARRAY);
|
||||
}
|
||||
|
||||
public MeshMSConversation nextConversation() throws ServalDInterfaceException, IOException
|
||||
{
|
||||
Object tok = nextJsonToken(reader);
|
||||
if (tok == JsonToken.END_ARRAY) {
|
||||
consume(reader, JsonToken.END_OBJECT);
|
||||
consume(reader, JsonToken.EOF);
|
||||
return null;
|
||||
}
|
||||
if (rowCount != 0) {
|
||||
match(tok, JsonToken.COMMA);
|
||||
tok = nextJsonToken(reader);
|
||||
}
|
||||
match(tok, JsonToken.START_ARRAY);
|
||||
Object[] row = new Object[headers.size()];
|
||||
for (int i = 0; i < headers.size(); ++i) {
|
||||
if (i != 0)
|
||||
consume(reader, JsonToken.COMMA);
|
||||
row[i] = consume(reader);
|
||||
}
|
||||
consume(reader, JsonToken.END_ARRAY);
|
||||
int _id = narrow(row[columnIndex__id], Integer.class);
|
||||
SubscriberId my_sid;
|
||||
try {
|
||||
my_sid = new SubscriberId(narrow(row[columnIndex_my_sid], String.class));
|
||||
}
|
||||
catch (SubscriberId.InvalidHexException e) {
|
||||
throw new ServalDInterfaceException("invalid JSON column value: my_sid", e);
|
||||
}
|
||||
SubscriberId their_sid;
|
||||
try {
|
||||
their_sid = new SubscriberId(narrow(row[columnIndex_their_sid], String.class));
|
||||
}
|
||||
catch (SubscriberId.InvalidHexException e) {
|
||||
throw new ServalDInterfaceException("invalid JSON column value: their_sid", e);
|
||||
}
|
||||
boolean is_read = narrow(row[columnIndex_read], Boolean.class);
|
||||
int last_message = narrow(row[columnIndex_last_message], Integer.class);
|
||||
int read_offset = narrow(row[columnIndex_read_offset], Integer.class);
|
||||
return new MeshMSConversation(rowCount++, _id, my_sid, their_sid, is_read, last_message, read_offset);
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
{
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
reader = null;
|
||||
}
|
||||
}
|
||||
|
||||
static void match(Object tok, JsonToken exactly) throws ServalDInterfaceException
|
||||
{
|
||||
if (tok != exactly)
|
||||
throw new ServalDInterfaceException("unexpected JSON token " + exactly + ", got: " + jsonTokenDescription(tok));
|
||||
}
|
||||
|
||||
static void consume(PushbackReader rd, JsonToken exactly) throws ServalDInterfaceException, IOException
|
||||
{
|
||||
match(nextJsonToken(rd), exactly);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> T narrow(Object tok, Class<T> cls) throws ServalDInterfaceException
|
||||
{
|
||||
assert !cls.isAssignableFrom(JsonToken.class); // can only narrow to values
|
||||
if (tok == JsonToken.EOF)
|
||||
throw new ServalDInterfaceException("unexpected EOF");
|
||||
if (tok instanceof JsonToken)
|
||||
throw new ServalDInterfaceException("expecting JSON " + cls.getName() + ", got: " + tok);
|
||||
// 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;
|
||||
throw new ServalDInterfaceException("expecting JSON " + cls.getName() + ", got: " + jsonTokenDescription(tok));
|
||||
}
|
||||
|
||||
static <T> T consume(PushbackReader rd, Class<T> cls) throws ServalDInterfaceException, IOException
|
||||
{
|
||||
return narrow(nextJsonToken(rd), cls);
|
||||
}
|
||||
|
||||
static Object consume(PushbackReader rd) throws ServalDInterfaceException, IOException
|
||||
{
|
||||
return consume(rd, Object.class);
|
||||
}
|
||||
|
||||
static String consume(PushbackReader rd, String exactly) throws ServalDInterfaceException, IOException
|
||||
{
|
||||
String tok = consume(rd, String.class);
|
||||
if (tok.equals(exactly))
|
||||
return tok;
|
||||
throw new ServalDInterfaceException("unexpected JSON String \"" + exactly + "\", got: " + jsonTokenDescription(tok));
|
||||
}
|
||||
|
||||
static <T> int consumeArray(PushbackReader rd, Collection<T> collection, Class<T> cls) throws ServalDInterfaceException, IOException
|
||||
{
|
||||
int added = 0;
|
||||
consume(rd, JsonToken.START_ARRAY);
|
||||
Object tok = nextJsonToken(rd);
|
||||
if (tok != JsonToken.END_ARRAY) {
|
||||
while (true) {
|
||||
try {
|
||||
collection.add(narrow(tok, cls));
|
||||
++added;
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
throw new ServalDInterfaceException("unexpected JSON token: " + jsonTokenDescription(tok));
|
||||
}
|
||||
tok = nextJsonToken(rd);
|
||||
if (tok == JsonToken.END_ARRAY)
|
||||
break;
|
||||
match(tok, JsonToken.COMMA);
|
||||
tok = nextJsonToken(rd);
|
||||
}
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
enum JsonToken {
|
||||
START_OBJECT,
|
||||
END_OBJECT,
|
||||
START_ARRAY,
|
||||
END_ARRAY,
|
||||
COMMA,
|
||||
COLON,
|
||||
NULL,
|
||||
EOF
|
||||
};
|
||||
|
||||
static boolean jsonIsToken(Object tok)
|
||||
{
|
||||
return tok instanceof JsonToken || tok instanceof String || tok instanceof Double || tok instanceof Integer || tok instanceof Boolean;
|
||||
}
|
||||
|
||||
static String jsonTokenDescription(Object tok)
|
||||
{
|
||||
if (tok instanceof String)
|
||||
return "\"" + tok + "\"";
|
||||
if (tok instanceof Number)
|
||||
return "" + tok;
|
||||
if (tok instanceof Boolean)
|
||||
return "" + tok;
|
||||
assert tok instanceof JsonToken;
|
||||
return tok.toString();
|
||||
}
|
||||
|
||||
static void readAll(Reader rd, char[] word) throws ServalDInterfaceException, IOException
|
||||
{
|
||||
int len = 0;
|
||||
while (len < word.length) {
|
||||
int n = rd.read(word, len, word.length - len);
|
||||
if (n == -1)
|
||||
throw new ServalDInterfaceException("unexpected EOF");
|
||||
len += n;
|
||||
}
|
||||
}
|
||||
|
||||
static Object nextJsonToken(PushbackReader rd) throws ServalDInterfaceException, IOException
|
||||
{
|
||||
while (true) {
|
||||
int c = rd.read();
|
||||
switch (c) {
|
||||
case -1:
|
||||
return JsonToken.EOF;
|
||||
case '\t':
|
||||
case '\r':
|
||||
case '\n':
|
||||
case ' ':
|
||||
break;
|
||||
case '{':
|
||||
return JsonToken.START_OBJECT;
|
||||
case '}':
|
||||
return JsonToken.END_OBJECT;
|
||||
case '[':
|
||||
return JsonToken.START_ARRAY;
|
||||
case ']':
|
||||
return JsonToken.END_ARRAY;
|
||||
case ',':
|
||||
return JsonToken.COMMA;
|
||||
case ':':
|
||||
return JsonToken.COLON;
|
||||
case 't': {
|
||||
char[] word = new char[3];
|
||||
readAll(rd, word);
|
||||
if (word[0] == 'r' && word[1] == 'u' && word[2] == 'e')
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
throw new ServalDInterfaceException("malformed JSON");
|
||||
case 'f': {
|
||||
char[] word = new char[4];
|
||||
readAll(rd, word);
|
||||
if (word[0] == 'a' && word[1] == 'l' && word[2] == 's' && word[3] == 'e')
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
throw new ServalDInterfaceException("malformed JSON");
|
||||
case 'n': {
|
||||
char[] word = new char[3];
|
||||
readAll(rd, word);
|
||||
if (word[0] == 'u' && word[1] == 'l' && word[2] == 'l')
|
||||
return JsonToken.NULL;
|
||||
}
|
||||
throw new ServalDInterfaceException("malformed JSON");
|
||||
case '"': {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean slosh = false;
|
||||
while (true) {
|
||||
c = rd.read();
|
||||
if (c == -1)
|
||||
throw new ServalDInterfaceException("unexpected EOF in JSON string");
|
||||
if (slosh) {
|
||||
switch (c) {
|
||||
case '"': case '/': case '\\': sb.append('"'); 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':
|
||||
char[] hex = new char[4];
|
||||
readAll(rd, hex);
|
||||
int code = Integer.valueOf(new String(hex), 16);
|
||||
if (code >= 0 && code <= 0xffff) {
|
||||
sb.append((char)code);
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
default:
|
||||
throw new ServalDInterfaceException("malformed JSON string");
|
||||
}
|
||||
}
|
||||
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 = rd.read();
|
||||
}
|
||||
if (c == '0') {
|
||||
sb.append((char)c);
|
||||
c = rd.read();
|
||||
}
|
||||
else if (Character.isDigit(c)) {
|
||||
do {
|
||||
sb.append((char)c);
|
||||
c = rd.read();
|
||||
}
|
||||
while (Character.isDigit(c));
|
||||
}
|
||||
else
|
||||
throw new ServalDInterfaceException("malformed JSON number");
|
||||
boolean isfloat = false;
|
||||
if (c == '.') {
|
||||
isfloat = true;
|
||||
sb.append((char)c);
|
||||
c = rd.read();
|
||||
if (c == -1)
|
||||
throw new ServalDInterfaceException("unexpected EOF in JSON number");
|
||||
if (!Character.isDigit(c))
|
||||
throw new ServalDInterfaceException("malformed JSON number");
|
||||
do {
|
||||
sb.append((char)c);
|
||||
c = rd.read();
|
||||
}
|
||||
while (Character.isDigit(c));
|
||||
}
|
||||
if (c == 'e' || c == 'E') {
|
||||
isfloat = true;
|
||||
sb.append((char)c);
|
||||
c = rd.read();
|
||||
if (c == '+' || c == '-') {
|
||||
sb.append((char)c);
|
||||
c = rd.read();
|
||||
}
|
||||
if (c == -1)
|
||||
throw new ServalDInterfaceException("unexpected EOF in JSON number");
|
||||
if (!Character.isDigit(c))
|
||||
throw new ServalDInterfaceException("malformed JSON number");
|
||||
do {
|
||||
sb.append((char)c);
|
||||
c = rd.read();
|
||||
}
|
||||
while (Character.isDigit(c));
|
||||
}
|
||||
rd.unread(c);
|
||||
String number = sb.toString();
|
||||
try {
|
||||
if (isfloat)
|
||||
return Double.parseDouble(number);
|
||||
else
|
||||
return Integer.parseInt(number);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
throw new ServalDInterfaceException("malformed JSON number: " + number);
|
||||
}
|
||||
}
|
||||
default:
|
||||
throw new ServalDInterfaceException("malformed JSON: '" + (char)c + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
72
java/org/servalproject/test/Meshms.java
Normal file
72
java/org/servalproject/test/Meshms.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.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;
|
||||
|
||||
public class Meshms {
|
||||
|
||||
static void meshms_list_conversations(SubscriberId sid, String offset, String count) throws ServalDInterfaceException, IOException, InterruptedException
|
||||
{
|
||||
ServalDClient client = ServalDClient.newServalDClient();
|
||||
MeshMSConversationList list = client.meshmsListConversations(sid);
|
||||
try {
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
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]), args.length > 2 ? args[2] : null, args.length > 3 ? args[3] : null);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
System.err.println("No such command: " + methodName);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
72
tests/meshmsjava
Executable file
72
tests/meshmsjava
Executable file
@ -0,0 +1,72 @@
|
||||
#!/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"
|
||||
|
||||
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 5
|
||||
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
|
||||
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"
|
||||
}
|
||||
|
||||
runTests "$@"
|
Loading…
x
Reference in New Issue
Block a user