mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-02-20 17:33:08 +00:00
MeshMS Java API: send message
This commit is contained in:
parent
819b8dc9e7
commit
eba7f6555f
@ -27,9 +27,18 @@ import org.servalproject.servaldna.meshms.MeshMSMessageList;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.HttpURLConnection;
|
||||
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.MeshMSCommon;
|
||||
import org.servalproject.servaldna.meshms.MeshMSConversationList;
|
||||
import org.servalproject.servaldna.meshms.MeshMSMessageList;
|
||||
import org.servalproject.servaldna.meshms.MeshMSException;
|
||||
import org.servalproject.servaldna.meshms.MeshMSStatus;
|
||||
|
||||
public class ServalDClient implements ServalDHttpConnectionFactory
|
||||
{
|
||||
@ -63,6 +72,11 @@ public class ServalDClient implements ServalDHttpConnectionFactory
|
||||
return list;
|
||||
}
|
||||
|
||||
public MeshMSStatus meshmsSendMessage(SubscriberId sid1, SubscriberId sid2, String text) throws IOException, ServalDInterfaceException, MeshMSException
|
||||
{
|
||||
return MeshMSCommon.sendMessage(this, sid1, sid2, text);
|
||||
}
|
||||
|
||||
// interface ServalDHttpConnectionFactory
|
||||
public HttpURLConnection newServalDHttpConnection(String path) throws ServalDInterfaceException, IOException
|
||||
{
|
||||
|
@ -21,8 +21,9 @@
|
||||
package org.servalproject.servaldna;
|
||||
|
||||
/**
|
||||
* Thrown when a request to a servald JNI method fails. This typically means that the returned
|
||||
* status is non-zero, or some other result was returned that indicated the operation failed.
|
||||
* Thrown when servald returns an error result from an operation, whether JNI or RESTful HTTP
|
||||
* request, or MDP command. This typically means that the returned status is non-zero, or some
|
||||
* other result was returned that indicated the operation failed.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
|
@ -93,6 +93,12 @@ public class ServerControl {
|
||||
if (!isRunning())
|
||||
throw new ServalDInterfaceException("server is not running");
|
||||
if (client==null) {
|
||||
/* TODO: replace the following username/password configuration with a better scheme
|
||||
* (outside the scope of this API) that does not require any invocations of the JNI, and
|
||||
* allows any application (user) on the local host to request authorisation to use the
|
||||
* RESTful interface. The authorisation must then be supplied to the restful client
|
||||
* object before requests can be made.
|
||||
*/
|
||||
String restfulPassword = ServalDCommand.getConfigItem("rhizome.api.restful.users." + restfulUsername + ".password");
|
||||
if (restfulPassword == null) {
|
||||
String pwd = new BigInteger(130, new SecureRandom()).toString(32);
|
||||
|
@ -22,51 +22,101 @@ package org.servalproject.servaldna.meshms;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
import org.servalproject.servaldna.SubscriberId;
|
||||
import org.servalproject.servaldna.ServalDHttpConnectionFactory;
|
||||
import org.servalproject.servaldna.ServalDInterfaceException;
|
||||
import org.servalproject.servaldna.ServalDFailureException;
|
||||
import org.servalproject.json.JSONTokeniser;
|
||||
import org.servalproject.json.JSONInputException;
|
||||
|
||||
class MeshMSCommon
|
||||
public class MeshMSCommon
|
||||
{
|
||||
protected static JSONTokeniser connectMeshMSRestful(HttpURLConnection conn) throws IOException, ServalDInterfaceException, MeshMSException
|
||||
protected static JSONTokeniser receiveRestfulResponse(HttpURLConnection conn, int expected_response_code) 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);
|
||||
}
|
||||
Status status = decodeRestfulStatus(json);
|
||||
throwRestfulResponseExceptions(status, conn.getURL());
|
||||
throw new ServalDInterfaceException("unexpected MeshMS status = " + status.meshms_status + ", \"" + status.message + "\"");
|
||||
}
|
||||
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK)
|
||||
if (conn.getResponseCode() != expected_response_code)
|
||||
throw new ServalDInterfaceException("unexpected HTTP response code: " + conn.getResponseCode());
|
||||
JSONTokeniser json = new JSONTokeniser(new InputStreamReader(conn.getInputStream(), "US-ASCII"));
|
||||
return json;
|
||||
}
|
||||
|
||||
private static class Status {
|
||||
public MeshMSStatus meshms_status;
|
||||
public String message;
|
||||
}
|
||||
|
||||
protected static Status decodeRestfulStatus(JSONTokeniser json) throws IOException, ServalDInterfaceException
|
||||
{
|
||||
try {
|
||||
Status status = new Status();
|
||||
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);
|
||||
status.message = 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);
|
||||
status.meshms_status = MeshMSStatus.fromCode(json.consume(Integer.class));
|
||||
json.consume(JSONTokeniser.Token.END_OBJECT);
|
||||
json.consume(JSONTokeniser.Token.EOF);
|
||||
return status;
|
||||
}
|
||||
catch (JSONInputException e) {
|
||||
throw new ServalDInterfaceException("malformed JSON status response", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void throwRestfulResponseExceptions(Status status, URL url) throws MeshMSException, ServalDFailureException
|
||||
{
|
||||
switch (status.meshms_status) {
|
||||
case OK:
|
||||
case UPDATED:
|
||||
break;
|
||||
case SID_LOCKED:
|
||||
throw new MeshMSUnknownIdentityException(url);
|
||||
case PROTOCOL_FAULT:
|
||||
throw new MeshMSProtocolFaultException(url);
|
||||
case ERROR:
|
||||
throw new ServalDFailureException("received meshms_status=ERROR(-1) from " + url);
|
||||
}
|
||||
}
|
||||
|
||||
public static MeshMSStatus sendMessage(ServalDHttpConnectionFactory connector, SubscriberId sid1, SubscriberId sid2, String text) throws IOException, ServalDInterfaceException, MeshMSException
|
||||
{
|
||||
HttpURLConnection conn = connector.newServalDHttpConnection("/restful/meshms/" + sid1.toHex() + "/" + sid2.toHex() + "/sendmessage");
|
||||
String boundary = Long.toHexString(System.currentTimeMillis());
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setDoOutput(true);
|
||||
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
|
||||
conn.connect();
|
||||
OutputStream ost = conn.getOutputStream();
|
||||
PrintStream wr = new PrintStream(ost, false, "US-ASCII");
|
||||
wr.print("--" + boundary + "\r\n");
|
||||
wr.print("Content-Disposition: form-data; name=\"message\"\r\n");
|
||||
wr.print("Content-Type: text/plain; charset=utf-8\r\n");
|
||||
wr.print("\r\n");
|
||||
wr.print(text);
|
||||
wr.print("\r\n--" + boundary + "--\r\n");
|
||||
wr.close();
|
||||
JSONTokeniser json = MeshMSCommon.receiveRestfulResponse(conn, HttpURLConnection.HTTP_CREATED);
|
||||
Status status = decodeRestfulStatus(json);
|
||||
throwRestfulResponseExceptions(status, conn.getURL());
|
||||
return status.meshms_status;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -69,7 +69,8 @@ public class MeshMSConversationList {
|
||||
columnIndex_read_offset = -1;
|
||||
rowCount = 0;
|
||||
httpConnection = httpConnector.newServalDHttpConnection("/restful/meshms/" + sid.toHex() + "/conversationlist.json");
|
||||
json = MeshMSCommon.connectMeshMSRestful(httpConnection);
|
||||
httpConnection.connect();
|
||||
json = MeshMSCommon.receiveRestfulResponse(httpConnection, HttpURLConnection.HTTP_OK);
|
||||
json.consume(JSONTokeniser.Token.START_OBJECT);
|
||||
json.consume("header");
|
||||
json.consume(JSONTokeniser.Token.COLON);
|
||||
|
@ -79,7 +79,8 @@ public class MeshMSMessageList {
|
||||
columnIndex_ack_offset = -1;
|
||||
rowCount = 0;
|
||||
httpConnection = httpConnector.newServalDHttpConnection("/restful/meshms/" + my_sid.toHex() + "/" + their_sid.toHex() + "/messagelist.json");
|
||||
json = MeshMSCommon.connectMeshMSRestful(httpConnection);
|
||||
httpConnection.connect();
|
||||
json = MeshMSCommon.receiveRestfulResponse(httpConnection, HttpURLConnection.HTTP_OK);
|
||||
json.consume(JSONTokeniser.Token.START_OBJECT);
|
||||
json.consume("read_offset");
|
||||
json.consume(JSONTokeniser.Token.COLON);
|
||||
|
63
java/org/servalproject/servaldna/meshms/MeshMSStatus.java
Normal file
63
java/org/servalproject/servaldna/meshms/MeshMSStatus.java
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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.ServalDInterfaceException;
|
||||
|
||||
/* This enum is a direct isomorphism from the C "enum meshms_result" defined in meshms.h.
|
||||
*/
|
||||
public enum MeshMSStatus {
|
||||
ERROR(-1), // unexpected error (underlying failure)
|
||||
OK(0), // operation succeeded, no bundle changed
|
||||
UPDATED(1), // operation succeeded, bundle updated
|
||||
SID_LOCKED(2), // cannot decode or send messages for that SID
|
||||
PROTOCOL_FAULT(3), // missing or faulty ply bundle
|
||||
;
|
||||
|
||||
final public int code;
|
||||
|
||||
private MeshMSStatus(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static MeshMSStatus fromCode(int code) throws InvalidException
|
||||
{
|
||||
MeshMSStatus status = null;
|
||||
switch (code) {
|
||||
case -1: status = ERROR; break;
|
||||
case 0: status = OK; break;
|
||||
case 1: status = UPDATED; break;
|
||||
case 2: status = SID_LOCKED; break;
|
||||
case 3: status = PROTOCOL_FAULT; break;
|
||||
default: throw new InvalidException(code);
|
||||
}
|
||||
assert status.code == code;
|
||||
return status;
|
||||
}
|
||||
|
||||
public static class InvalidException extends ServalDInterfaceException
|
||||
{
|
||||
public InvalidException(int code) {
|
||||
super("invalid MeshMS status code = " + code);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -20,6 +20,7 @@
|
||||
|
||||
package org.servalproject.test;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.servalproject.servaldna.ServalDClient;
|
||||
import org.servalproject.servaldna.ServalDInterfaceException;
|
||||
import org.servalproject.servaldna.ServerControl;
|
||||
@ -29,8 +30,7 @@ import org.servalproject.servaldna.meshms.MeshMSConversationList;
|
||||
import org.servalproject.servaldna.meshms.MeshMSException;
|
||||
import org.servalproject.servaldna.meshms.MeshMSMessage;
|
||||
import org.servalproject.servaldna.meshms.MeshMSMessageList;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.servalproject.servaldna.meshms.MeshMSStatus;
|
||||
|
||||
public class Meshms {
|
||||
|
||||
@ -94,6 +94,19 @@ public class Meshms {
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
static void meshms_send_message(SubscriberId sid1, SubscriberId sid2, String text) throws ServalDInterfaceException, IOException, InterruptedException
|
||||
{
|
||||
ServalDClient client = new ServerControl().getRestfulClient();
|
||||
try {
|
||||
MeshMSStatus status = client.meshmsSendMessage(sid1, sid2, text);
|
||||
System.out.println("" + status);
|
||||
}
|
||||
catch (MeshMSException e) {
|
||||
System.out.println(e.toString());
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
public static void main(String... args)
|
||||
{
|
||||
if (args.length < 1)
|
||||
@ -104,6 +117,8 @@ public class Meshms {
|
||||
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]));
|
||||
else if (methodName.equals("meshms-send-message"))
|
||||
meshms_send_message(new SubscriberId(args[1]), new SubscriberId(args[2]), args[3]);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
|
@ -137,14 +137,30 @@ test_MeshmsListMessages() {
|
||||
}
|
||||
|
||||
doc_MeshmsListMessagesNoIdentity="Java API list MeshMS messages from unknown identity"
|
||||
setup_MeshmsListMessagesNoIdentity() {
|
||||
setup
|
||||
SIDX=0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
|
||||
}
|
||||
test_MeshmsListMessagesNoIdentity() {
|
||||
SIDX=0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
|
||||
executeJavaOk org.servalproject.test.Meshms meshms-list-messages $SIDX $SIDA2
|
||||
assertStdoutGrep 'MeshMSUnknownIdentityException'
|
||||
tfw_cat --stdout --stderr
|
||||
}
|
||||
|
||||
doc_MeshmsSend="Java API send MeshMS message"
|
||||
test_MeshmsSend() {
|
||||
executeJavaOk org.servalproject.test.Meshms meshms-send-message $SIDA1 $SIDA2 "Hello World"
|
||||
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
||||
assertStdoutGrep --matches=1 ':>:Hello World'
|
||||
executeJavaOk org.servalproject.test.Meshms meshms-send-message $SIDA2 $SIDA1 "Hello Back!"
|
||||
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
||||
assertStdoutGrep --matches=1 ':>:Hello World$'
|
||||
assertStdoutGrep --matches=1 ':<:Hello Back!$'
|
||||
}
|
||||
|
||||
doc_MeshmsSendNoIdentity="Java API send MeshMS message from unknown identity"
|
||||
test_MeshmsSendNoIdentity() {
|
||||
SIDX=0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
|
||||
executeJavaOk org.servalproject.test.Meshms meshms-send-message $SIDX $SIDA2 "Hello World"
|
||||
assertStdoutGrep 'MeshMSUnknownIdentityException'
|
||||
tfw_cat --stdout --stderr
|
||||
}
|
||||
|
||||
runTests "$@"
|
||||
|
Loading…
x
Reference in New Issue
Block a user