Trying out RESTful API
parent
0cb5d5fbb5
commit
30db2182a3
|
@ -1,7 +1,9 @@
|
||||||
package meerkat.bulletinboard.sqlserver;
|
package meerkat.bulletinboard.sqlserver;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
|
|
||||||
|
@ -18,11 +20,11 @@ import meerkat.protobuf.Voting.MessageFilterList;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.protobuf.Crypto.SignatureVerificationKey;
|
import meerkat.protobuf.Crypto.SignatureVerificationKey;
|
||||||
import meerkat.protobuf.Voting.BulletinBoardMessage;
|
import meerkat.protobuf.Voting.BulletinBoardMessage;
|
||||||
|
import meerkat.protobuf.Voting.BulletinBoardMessageList;
|
||||||
import meerkat.crypto.Digest;
|
import meerkat.crypto.Digest;
|
||||||
import meerkat.crypto.concrete.SHA256Digest;
|
import meerkat.crypto.concrete.SHA256Digest;
|
||||||
import meerkat.protobuf.Voting.MessageID;
|
import meerkat.protobuf.Voting.MessageID;
|
||||||
|
|
||||||
@Path("/SQLServer")
|
|
||||||
public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
|
public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
|
||||||
|
|
||||||
protected Connection connection;
|
protected Connection connection;
|
||||||
|
@ -67,7 +69,6 @@ public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
|
||||||
*/
|
*/
|
||||||
protected abstract void insertNewTags(String[] tags) throws SQLException;
|
protected abstract void insertNewTags(String[] tags) throws SQLException;
|
||||||
|
|
||||||
@POST
|
|
||||||
@Override
|
@Override
|
||||||
public boolean postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
public boolean postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||||
if (!verifyMessage(msg)) {
|
if (!verifyMessage(msg)) {
|
||||||
|
@ -88,11 +89,11 @@ public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
sql = "INSERT INTO MsgTable (Id, Data, Signature) VALUES(?,?,?)";
|
sql = "INSERT INTO MsgTable (MsgId, Msg, SignerId) VALUES(?,?,?)";
|
||||||
pstmt = connection.prepareStatement(sql);
|
pstmt = connection.prepareStatement(sql);
|
||||||
pstmt.setBytes(1, msgID);
|
pstmt.setBytes(1, msgID);
|
||||||
pstmt.setBytes(2, msg.getMsg().getData().toByteArray());
|
pstmt.setBytes(2, msg.toByteArray());
|
||||||
pstmt.setBytes(3, msg.getSig().toByteArray());
|
pstmt.setBytes(3, msg.getSig().getSignerId().toByteArray());
|
||||||
pstmt.executeUpdate();
|
pstmt.executeUpdate();
|
||||||
pstmt.close();
|
pstmt.close();
|
||||||
|
|
||||||
|
@ -113,7 +114,7 @@ public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
|
||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
sql = "INSERT INTO MsgTagTable (TagId, MsgId) SELECT TagTable.Id, ? AS MsgId FROM TagTable WHERE tag = ?";
|
sql = "INSERT INTO MsgTagTable (TagId, MsgId) SELECT TagTable.TagId, ? AS MsgId FROM TagTable WHERE Tag = ?";
|
||||||
pstmt = connection.prepareStatement(sql);
|
pstmt = connection.prepareStatement(sql);
|
||||||
|
|
||||||
for (String tag : tags){
|
for (String tag : tags){
|
||||||
|
@ -133,7 +134,7 @@ public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<BulletinBoardMessage> readMessages(MessageFilterList filterList, int max) {
|
public BulletinBoardMessageList readMessages(MessageFilterList filterList) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -149,22 +150,22 @@ public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
// read the result set
|
// read the result set
|
||||||
s += "entry = " + rs.getInt("EntryNum") + " \n";
|
s += "entry = " + rs.getInt("EntryNum") + " \n";
|
||||||
s += "id = " + new String(rs.getBytes("Id")) + " \n";
|
s += "id = " + Arrays.toString(rs.getBytes("MsgId")) + " \n";
|
||||||
s += "data = " + new String(rs.getBytes("Data")) + " \n";
|
s += "msg = " + Arrays.toString(rs.getBytes("Msg")) + " \n";
|
||||||
s += "signature = " + new String(rs.getBytes("Signature")) + "\t\n<BR>";
|
s += "signer ID = " + Arrays.toString(rs.getBytes("SignerId")) + "\t\n<BR>";
|
||||||
}
|
}
|
||||||
|
|
||||||
rs = statement.executeQuery("select * from TagTable");
|
rs = statement.executeQuery("select * from TagTable");
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
// read the result set
|
// read the result set
|
||||||
s += "Tag = " + rs.getString("Tag") + " \n";
|
s += "Tag = " + rs.getString("Tag") + " \n";
|
||||||
s += "TagId = " + rs.getInt("Id") + "\t\n<BR>";
|
s += "TagId = " + rs.getInt("TagId") + "\t\n<BR>";
|
||||||
}
|
}
|
||||||
|
|
||||||
rs = statement.executeQuery("select * from MsgTagTable");
|
rs = statement.executeQuery("select * from MsgTagTable");
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
// read the result set
|
// read the result set
|
||||||
s += "MsgId = " + new String(rs.getBytes("MsgId")) + " \n";
|
s += "MsgId = " + Arrays.toString(rs.getBytes("MsgId")) + " \n";
|
||||||
s += "TagId = " + rs.getInt("TagId") + "\t\n<BR>";
|
s += "TagId = " + rs.getInt("TagId") + "\t\n<BR>";
|
||||||
}
|
}
|
||||||
} catch(SQLException e){
|
} catch(SQLException e){
|
||||||
|
|
|
@ -6,12 +6,24 @@ import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.annotation.PreDestroy;
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
import meerkat.protobuf.Voting.MessageFilterList;
|
import meerkat.protobuf.Voting.MessageFilterList;
|
||||||
|
import meerkat.rest.Constants;
|
||||||
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.protobuf.Voting.BulletinBoardMessage;
|
import meerkat.protobuf.Voting.BulletinBoardMessage;
|
||||||
|
import meerkat.protobuf.Voting.BulletinBoardMessageList;
|
||||||
|
|
||||||
|
|
||||||
|
@Path("/SQLServer")
|
||||||
public class SQLiteBulletinBoardServer extends BulletinBoardSQLServer {
|
public class SQLiteBulletinBoardServer extends BulletinBoardSQLServer {
|
||||||
|
|
||||||
protected static final int TIMEOUT = 20;
|
protected static final int TIMEOUT = 20;
|
||||||
|
@ -20,8 +32,8 @@ public class SQLiteBulletinBoardServer extends BulletinBoardSQLServer {
|
||||||
* This procedure initializes:
|
* This procedure initializes:
|
||||||
* 1. The database connection
|
* 1. The database connection
|
||||||
* 2. The database tables (if they do not yet exist).
|
* 2. The database tables (if they do not yet exist).
|
||||||
* 3.
|
|
||||||
*/
|
*/
|
||||||
|
@PostConstruct
|
||||||
@Override
|
@Override
|
||||||
public void init() throws CommunicationException {
|
public void init() throws CommunicationException {
|
||||||
|
|
||||||
|
@ -31,9 +43,9 @@ public class SQLiteBulletinBoardServer extends BulletinBoardSQLServer {
|
||||||
Statement statement = connection.createStatement();
|
Statement statement = connection.createStatement();
|
||||||
statement.setQueryTimeout(TIMEOUT);
|
statement.setQueryTimeout(TIMEOUT);
|
||||||
|
|
||||||
statement.executeUpdate("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INTEGER PRIMARY KEY, Id BLOB UNIQUE, Data BLOB, Signature BLOB)");
|
statement.executeUpdate("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INTEGER PRIMARY KEY, MsgId BLOB UNIQUE, Msg BLOB, SignerId BLOB)");
|
||||||
statement.executeUpdate("CREATE TABLE IF NOT EXISTS TagTable (Id INTEGER PRIMARY KEY, Tag varchar(50) UNIQUE)");
|
statement.executeUpdate("CREATE TABLE IF NOT EXISTS TagTable (TagId INTEGER PRIMARY KEY, Tag varchar(50) UNIQUE)");
|
||||||
statement.executeUpdate("CREATE TABLE IF NOT EXISTS MsgTagTable (MsgId BLOB, TagId INTEGER, FOREIGN KEY (MsgId) REFERENCES MsgTable(Id), FOREIGN KEY (TagId) REFERENCES TagTable(Id))");
|
statement.executeUpdate("CREATE TABLE IF NOT EXISTS MsgTagTable (MsgId BLOB, TagId INTEGER, FOREIGN KEY (MsgId) REFERENCES MsgTable(MsgId), FOREIGN KEY (TagId) REFERENCES TagTable(TagId))");
|
||||||
|
|
||||||
statement.close();
|
statement.close();
|
||||||
|
|
||||||
|
@ -46,14 +58,8 @@ public class SQLiteBulletinBoardServer extends BulletinBoardSQLServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<BulletinBoardMessage> readMessages(MessageFilterList filterList, int max) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@PreDestroy
|
||||||
public void close() throws CommunicationException{
|
public void close() throws CommunicationException{
|
||||||
|
|
||||||
try{
|
try{
|
||||||
|
@ -91,5 +97,25 @@ public class SQLiteBulletinBoardServer extends BulletinBoardSQLServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @POST
|
||||||
|
@Consumes(Constants.MEDIATYPE_PROTOBUF)
|
||||||
|
@Override
|
||||||
|
public boolean postMessage(BulletinBoardMessage msg) throws CommunicationException {
|
||||||
|
return super.postMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @GET
|
||||||
|
@Consumes(Constants.MEDIATYPE_PROTOBUF)
|
||||||
|
@Produces(Constants.MEDIATYPE_PROTOBUF)
|
||||||
|
@Override
|
||||||
|
public BulletinBoardMessageList readMessages(MessageFilterList filterList) {
|
||||||
|
return super.readMessages(filterList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public String test(){
|
||||||
|
return "hello";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package meerkat.bulletinboard.webapp;
|
package meerkat.bulletinboard.webapp;
|
||||||
|
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.Message;
|
import com.google.protobuf.Message;
|
||||||
import meerkat.bulletinboard.service.HelloProtoBuf;
|
import meerkat.bulletinboard.service.HelloProtoBuf;
|
||||||
|
@ -17,38 +16,37 @@ import javax.ws.rs.Produces;
|
||||||
|
|
||||||
@Path("/proto")
|
@Path("/proto")
|
||||||
public class HelloProtoWebApp {
|
public class HelloProtoWebApp {
|
||||||
private HelloProtoBuf helloProtoBuf;
|
private HelloProtoBuf helloProtoBuf;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init(){
|
public void init() {
|
||||||
//helloProtoBuf = new HelloProtoBuf();
|
// helloProtoBuf = new HelloProtoBuf();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces(Constants.MEDIATYPE_PROTOBUF)
|
@Produces(Constants.MEDIATYPE_PROTOBUF)
|
||||||
public Message hello() {
|
public Message hello() {
|
||||||
byte[] b1 = {(byte) 1, (byte)2, (byte) 3, (byte) 4};
|
byte[] b1 = { (byte) 1, (byte) 2, (byte) 3, (byte) 4 };
|
||||||
byte[] b2 = {(byte) 11, (byte)12, (byte) 13, (byte) 14};
|
byte[] b2 = { (byte) 11, (byte) 12, (byte) 13, (byte) 14 };
|
||||||
|
byte[] b3 = {(byte) 21, (byte)22, (byte) 23, (byte) 24};
|
||||||
Message msg;
|
|
||||||
|
Message msg;
|
||||||
if (helloProtoBuf != null){
|
|
||||||
msg = helloProtoBuf.sayHello();
|
if (helloProtoBuf != null) {
|
||||||
}
|
msg = helloProtoBuf.sayHello();
|
||||||
else{
|
} else {
|
||||||
msg = BulletinBoardMessage.newBuilder()
|
msg = BulletinBoardMessage.newBuilder()
|
||||||
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
|
||||||
.addTags("signature")
|
.addTags("Signature")
|
||||||
.addTags("Trustee")
|
.addTags("Trustee")
|
||||||
.setData(ByteString.copyFrom(b1))
|
.setData(ByteString.copyFrom(b1)).build())
|
||||||
.build())
|
|
||||||
.setSig(Signature.newBuilder()
|
.setSig(Signature.newBuilder()
|
||||||
.setType(SignatureType.DSA)
|
.setType(SignatureType.DSA)
|
||||||
.setData(ByteString.copyFrom(b2))
|
.setData(ByteString.copyFrom(b2))
|
||||||
.build())
|
.setSignerId(ByteString.copyFrom(b3)).build())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,23 +3,37 @@ package meerkat.bulletinboard.webapp;
|
||||||
|
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.client.Client;
|
||||||
|
import javax.ws.rs.client.ClientBuilder;
|
||||||
|
import javax.ws.rs.client.Entity;
|
||||||
|
import javax.ws.rs.client.WebTarget;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
|
||||||
|
import meerkat.rest.Constants;
|
||||||
|
import meerkat.rest.Constants.*;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
|
||||||
import meerkat.bulletinboard.sqlserver.SQLiteBulletinBoardServer;
|
import meerkat.bulletinboard.sqlserver.SQLiteBulletinBoardServer;
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.protobuf.Crypto.*;
|
import meerkat.protobuf.Crypto.*;
|
||||||
import meerkat.protobuf.Voting.*;
|
import meerkat.protobuf.Voting.*;
|
||||||
|
import meerkat.rest.*;
|
||||||
|
|
||||||
@Path("/test")
|
@Path("/test")
|
||||||
public class SQLiteServerTest {
|
public class SQLiteServerTest {
|
||||||
|
|
||||||
|
private static String PROP_GETTY_URL = "gretty.httpBaseURI";
|
||||||
|
private static String BASE_URL = "http://localhost:8081";//System.getProperty(PROP_GETTY_URL);
|
||||||
|
private static String SQL_SERVER_URL = BASE_URL + "/SQLServer";
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
public Response main(){
|
public Response main(){
|
||||||
byte[] b1 = {(byte) 1, (byte)2, (byte) 3, (byte) 4};
|
byte[] b1 = {(byte) 1, (byte)2, (byte) 3, (byte) 4};
|
||||||
byte[] b2 = {(byte) 11, (byte)12, (byte) 13, (byte) 14};
|
byte[] b2 = {(byte) 11, (byte)12, (byte) 13, (byte) 14};
|
||||||
|
byte[] b3 = {(byte) 21, (byte)22, (byte) 23, (byte) 24};
|
||||||
|
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
|
@ -34,23 +48,34 @@ public class SQLiteServerTest {
|
||||||
.setSig(Signature.newBuilder()
|
.setSig(Signature.newBuilder()
|
||||||
.setType(SignatureType.DSA)
|
.setType(SignatureType.DSA)
|
||||||
.setData(ByteString.copyFrom(b2))
|
.setData(ByteString.copyFrom(b2))
|
||||||
|
.setSignerId(ByteString.copyFrom(b3))
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
SQLiteBulletinBoardServer bbs = new SQLiteBulletinBoardServer();
|
// SQLiteBulletinBoardServer bbs = new SQLiteBulletinBoardServer();
|
||||||
|
//
|
||||||
|
//
|
||||||
bbs.init();
|
// bbs.init();
|
||||||
|
//
|
||||||
|
// bbs.postMessage(msg);
|
||||||
|
// response = Response.status(Status.OK).entity(bbs.testPrint()).build();
|
||||||
|
//
|
||||||
|
// bbs.close();
|
||||||
|
|
||||||
bbs.postMessage(msg);
|
|
||||||
response = Response.status(Status.OK).entity(bbs.testPrint()).build();
|
|
||||||
|
|
||||||
bbs.close();
|
Client client = ClientBuilder.newClient()
|
||||||
|
.register(ProtobufMessageBodyWriter.class)
|
||||||
|
.register(ProtobufMessageBodyWriter.class);
|
||||||
|
WebTarget webTarget = client.target(SQL_SERVER_URL);
|
||||||
|
response = webTarget.request(MediaType.TEXT_HTML).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
|
||||||
|
|
||||||
} catch(CommunicationException e){
|
} catch(Exception e){
|
||||||
response = Response.status(Status.OK).entity(e.getMessage()).build();;
|
response = Response.status(Status.OK).entity(e.getMessage()).build();;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// response = Response.status(Status.OK).entity("OK").build();
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package meerkat.bulletinboard;
|
package meerkat.bulletinboard;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import meerkat.comm.CommunicationException;
|
import meerkat.comm.CommunicationException;
|
||||||
import meerkat.protobuf.Voting.BulletinBoardMessage;
|
import meerkat.protobuf.Voting.BulletinBoardMessage;
|
||||||
|
import meerkat.protobuf.Voting.BulletinBoardMessageList;
|
||||||
import meerkat.protobuf.Voting.MessageFilterList;
|
import meerkat.protobuf.Voting.MessageFilterList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,11 +31,10 @@ public interface BulletinBoardServer{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read all messages posted matching the given filter.
|
* Read all messages posted matching the given filter.
|
||||||
* @param filter return only messages that match the filter (null means no filtering).
|
* @param filter return only messages that match the filter (empty list means no filtering).
|
||||||
* @param max maximum number of messages to return (0=no limit)
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
List<BulletinBoardMessage> readMessages(MessageFilterList filterList, int max);
|
BulletinBoardMessageList readMessages(MessageFilterList filterList);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method closes the connection to the DB.
|
* This method closes the connection to the DB.
|
||||||
|
|
|
@ -28,12 +28,19 @@ message BulletinBoardMessage {
|
||||||
meerkat.Signature sig = 2;
|
meerkat.Signature sig = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message BulletinBoardMessageList {
|
||||||
|
|
||||||
|
repeated BulletinBoardMessage messages = 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
enum FilterType {
|
enum FilterType {
|
||||||
ID = 0; // Match exact message ID
|
ID = 0; // Match exact message ID
|
||||||
EXACT_ENTRY = 1; // Match exact entry number in database (chronological)
|
EXACT_ENTRY = 1; // Match exact entry number in database (chronological)
|
||||||
MAX_ENTRY = 2; // Find all entries in database up to specified entry number (chronological)
|
MAX_ENTRY = 2; // Find all entries in database up to specified entry number (chronological)
|
||||||
SIGNATURE = 3; // Find all entries in database that correspond to specific signature (signer)
|
SIGNATURE = 3; // Find all entries in database that correspond to specific signature (signer)
|
||||||
TAG = 4; // Find all entries in database that have a specific tag
|
TAG = 4; // Find all entries in database that have a specific tag
|
||||||
|
MAX_MESSAGES = 5; // Return at most some specified number of messages
|
||||||
}
|
}
|
||||||
|
|
||||||
message MessageFilter {
|
message MessageFilter {
|
||||||
|
|
Loading…
Reference in New Issue