It's a quite specific question but after days stuck in the same place and not getting any response in the Alljoyn forum, I decided to post it here. Maybe someone worked with this framework.
I am doing a project and I need the use of the signal mechanish that Alljoyn framework provides. However, I do need the signals inside a session and the example provides in the api core is sessionless.
In my case I need the service (server) to raise a signal and the client to receive it, but I am getting this error. Following the api samples, I managed to create a simple app that exchange messages through the methods defined in the interface, so I know that the communication is working. In this case, the service waits until the client connects and the send the signal. The client joins the session and right after register the signal and I am getting the BUS_NO_SUCH_INTERFACE error. I tried also to register after and before sending the signal, same problem. I think that for some reason the client does not find the bussignalhandler but I don't know why. I also put it in an external class and it didn't work.
I'm following the example in the core api guide:
https://allseenalliance.org/developers/develop/api-guide/core/android
This is the part where the service register and emit the signal:
SignalEmitter emitter = new SignalEmitter(mySignalInterface, joinerName,
sessionId,
SignalEmitter.GlobalBroadcast.Off);
myInterface = emitter.getInterface(SampleInterface.class);
// Emitting signals myInterface.buttonClicked(1);
myInterface.playerPosition(12, 1, -24);
However, in that example, I can't see a definition for myInterface. and I know it is not a mistake and they meant mySignalInterface because the method getInterface asks for an interface object and mySignalInterface is a class which implements that interface.
I put here the example I created and I'll upload the files in case someone wants to try them.
#BusInterface(name = "org.alljoyn.bus.samples.simple.SimpleInterface")
public interface SimpleInterface {
#BusMethod
String Ping(String inStr) throws BusException;
#BusSignal
public void playerPosition(int x, int y, int z) throws BusException;
}
Service:
Class inside the service to implement the signal and method
class SimpleService implements SimpleInterface, BusObject {
public String Ping(String inStr) {
mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_PING, inStr));
return inStr;
}
public void playerPosition(int x, int y, int z) { /* no implementation needed here*/}
}
Here part of the code that makes the connection in the service:
case CONNECT: {
org.alljoyn.bus.alljoyn.DaemonInit.PrepareDaemon(getApplicationContext());
mBus = new BusAttachment(getPackageName(), BusAttachment.RemoteMessage.Receive);
mBus.registerBusListener(new BusListener());
Status status = mBus.registerBusObject(mSimpleService, "/SimpleService");
if (status != Status.OK) {...}
status = mBus.connect();
logStatus("BusAttachment.connect()", status);
if (status != Status.OK) {...}
int flag = 0;
status = mBus.requestName(SERVICE_NAME, flag);
logStatus(String.format("BusAttachment.requestName(%s, 0x%08x)", SERVICE_NAME, flag), status);
if (status == Status.OK) {
status = mBus.advertiseName(SERVICE_NAME, SessionOpts.TRANSPORT_ANY);
logStatus(String.format("BusAttachement.advertiseName(%s)", SERVICE_NAME), status);
if (status != Status.OK) {...}
}
Mutable.ShortValue contactPort = new Mutable.ShortValue(CONTACT_PORT);
SessionOpts sessionOpts = new SessionOpts();
sessionOpts.traffic = SessionOpts.TRAFFIC_MESSAGES;
sessionOpts.isMultipoint = false;
sessionOpts.proximity = SessionOpts.PROXIMITY_ANY;
sessionOpts.transports = SessionOpts.TRANSPORT_ANY + SessionOpts.TRANSPORT_WFD;
status = mBus.bindSessionPort(contactPort, sessionOpts, new SessionPortListener() {
#Override
public boolean acceptSessionJoiner(short sessionPort, String joiner, SessionOpts sessionOpts) {
return sessionPort == CONTACT_PORT;
}
#Override
public void sessionJoined(short port, int id, String s) {
sessionId = id; joinerName = s; sessionEstablished = true;
}
});
logStatus(String.format("BusAttachment.bindSessionPort(%d, %s)",
contactPort.value, sessionOpts.toString()), status);
if (status != Status.OK) {...}
try {
while (!sessionEstablished) {
Thread.sleep(10);
}
SignalEmitter emitter = new SignalEmitter(mSimpleService, joinerName, sessionId, SignalEmitter.GlobalBroadcast.Off);
SimpleInterface myInterface = emitter.getInterface(SimpleInterface.class);
myInterface.playerPosition(12,1,1);
}
catch (BusException ex) {... }
}
Client:
/*On create of the activity which has a button and a text view to send text to the server */
mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_NULL
&& event.getAction() == KeyEvent.ACTION_UP) {
/* Call the remote object's Ping method. */
Message msg = mBusHandler.obtainMessage(BusHandler.PING,
view.getText().toString());
mBusHandler.sendMessage(msg);
}
return true;
}
});
private static final String SERVICE_NAME = "org.alljoyn.bus.samples.simple";
private static final short CONTACT_PORT=42;
private BusAttachment mBus;
private ProxyBusObject mProxyObj;
private SimpleInterface mSimpleInterface;
case CONNECT: {
org.alljoyn.bus.alljoyn.DaemonInit.PrepareDaemon(getApplicationContext());
mBus = new BusAttachment(getPackageName(), BusAttachment.RemoteMessage.Receive);
mBus.registerBusListener(new BusListener() {
#Override
public void foundAdvertisedName(String name, short transport, String namePrefix) {
if(!mIsConnected) {
Message msg = obtainMessage(JOIN_SESSION);
msg.arg1 = transport;
msg.obj = name;
sendMessage(msg);
}
}
});
Status status = mBus.connect();
logStatus("BusAttachment.connect()", status);
if (Status.OK != status) {...}
status = mBus.findAdvertisedName(SERVICE_NAME);
logStatus(String.format("BusAttachement.findAdvertisedName(%s)", SERVICE_NAME), status);
if (Status.OK != status) {...}
break;
}
case (JOIN_SESSION): {
if (mIsStoppingDiscovery) {
break;
}
short contactPort = CONTACT_PORT;
SessionOpts sessionOpts = new SessionOpts();
sessionOpts.transports = (short)msg.arg1;
Mutable.IntegerValue sessionId = new Mutable.IntegerValue();
Status status = mBus.joinSession((String) msg.obj, contactPort, sessionId, sessionOpts, new SessionListener() {
#Override
public void sessionLost(int sessionId, int reason) {
mIsConnected = false;
logInfo(String.format("MyBusListener.sessionLost(sessionId = %d, reason = %d)", sessionId,reason));
mHandler.sendEmptyMessage(MESSAGE_START_PROGRESS_DIALOG);
}
});
if (status == Status.OK) {
mProxyObj = mBus.getProxyBusObject(SERVICE_NAME,
"/SimpleService",
sessionId.value,
new Class<?>[] { SimpleInterface.class });
mSimpleInterface = mProxyObj.getInterface(SimpleInterface.class);
mSessionId = sessionId.value;
mIsConnected = true;
mHandler.sendEmptyMessage(MESSAGE_STOP_PROGRESS_DIALOG);
}
break;
status = mBus.registerSignalHandlers(this);
if (status != Status.OK) {...}
}
case PING: {
try {
if (mSimpleInterface != null) {
sendUiMessage(MESSAGE_PING, msg.obj);
String reply = mSimpleInterface.Ping((String) msg.obj);
sendUiMessage(MESSAGE_PING_REPLY, reply);
} catch {...}
}
...here some more code...
#BusSignalHandler(iface="org.alljoyn.bus.samples.simple.SimpleInterface", signal="playerPosition")
public void playerPosition(int x, int y, int z) {
sendUiMessage(MESSAGE_POST_TOAST, "Signal captured");
}
In this example, I have my client which has a textview where I can add text and send a ping to the server. This is working if I get rid of the registering signal part. I tried to do it as the api core says, with the Thread.sleep but doesn't work either.
Here I add the code of both of my applications (client & server) based on the samples that are in the alljoyn api.
https://github.com/JavierT/Alljoyn_signal_sample
Please let me know if you have some doubts, it was hard to put all the information in one post.
Thank you in advance.
I think this should solve your problem
Alljoyn BusSignalHandler
either of the following may be used to annotate a signal handler:
#BusSignalHandler(iface = "org.sample.MyInterface", signal = "MySignal")
public void handleSignal(String str)
{
}
#BusSignalHandler(iface = "org.myapp.IMyInterface", signal = "EmitMySignal")
public void handleSignal(String str)
{
}
The first example may be used succesfully when IMyInterface is known to the BusAttachment via a previous call to BusAttachment.registerBusObject(BusObject, String) or BusAttachment.getProxyBusObject(String, String, int, Class[]).
The second example may be used succesfully when IMyInterface is unknown to the BusAttachment.
using the second example should solve your problem.
Also, in your Client program as per the link provided above,
#BusSignalHandler(iface="org.alljoyn.bus.samples.simple.SimpleInterface", signal="playerPosition")
should be replaced with
#BusSignalHandler(iface="org.alljoyn.bus.samples.simpleclient.SimpleInterface", signal="playerPosition")
When you call registerSignalHandlers you must pass an object that implements the interface SimpleInterface and the method playerPosition will be called in that object when you receives a signal.
I believe (I've read the code quickly, I hope I'm not wrong) that you pass your BusHandler object, which does not implements SimpleInterface.
Please change :
private static final String SERVICE_NAME = "org.alljoyn.bus.samples.simple";
to
"org.alljoyn.bus.samples.simple.SimpleInterface" as you have interface name ,
both should be same . as when the Get method of BusObject is called the interface names does not match.
Related
I'm trying to build a system in which I can connect some devices to a server over the internet.
I want to stream some data over CoAP (10-30FPS), frame size = 3KB.
Firstly, I used Aiocoap, it sends up to 100FPS but uses too much CPU,
requests are NON, got low lose rate in Aiocoap,
while Eclipse/Californium could not send more than 3FPS,
when i use higher FPS, either I receive only the first block of each message or receiving nothing, also not ordered most of the times.
I was wondering if this is the real performance of Californium or am I using it in a wrong way?
I will share some code:
server.java
static class CoapObserverServer extends CoapResource {
int i = -1;
public CoapObserverServer() {
super("alarm");
setObservable(true); // enable observing
setObserveType(Type.NON); // configure the notification type to CONs
getAttributes().setObservable(); // mark observable in the Link-Format
System.out.println(this);
// schedule a periodic update task, otherwise let events call changed()
//new Timer().schedule(new UpdateTask(), 0, 1000/2);
}
private class UpdateTask extends TimerTask {
#Override
public void run() {
changed(); // notify all observers
}
}
#Override
public void handleGET(CoapExchange exchange) {
// the Max-Age value should match the update interval
exchange.setMaxAge(1);
//++i;
int leng = 2000;
String s = "" + i + "-" + fillString('X', leng - 1 - Integer.toString(i).len>
exchange.respond(s);
}
public static String fillString(char fillChar, int count){
// creates a string of 'x' repeating characters
char[] chars = new char[count];
while (count>0) chars[--count] = fillChar;
return new String(chars);
}
#Override
public void handleDELETE(CoapExchange exchange) {
delete(); // will also call clearAndNotifyObserveRelations(ResponseCode.NOT_>
exchange.respond(ResponseCode.DELETED);
}
#Override
public void handlePUT(CoapExchange exchange) {
exchange.accept();
int format = exchange.getRequestOptions().getContentFormat();
if (format == MediaTypeRegistry.TEXT_PLAIN) {
// ...
String plain = exchange.getRequestText();
try{
i = Integer.valueOf(plain);
} catch(NumberFormatException ex){
System.out.println("error converting string"+ plain);
}
exchange.respond(ResponseCode.CHANGED);
changed(); // notify all observers
}
}
Observer.java
private static final File CONFIG_FILE = new File("Californium3.properties");
private static final String CONFIG_HEADER = "Californium CoAP Properties file for client";
private static final int DEFAULT_MAX_RESOURCE_SIZE = 2 * 1024 * 1024; // 2 MB
private static final int DEFAULT_BLOCK_SIZE = 512;
static {
CoapConfig.register();
UdpConfig.register();
}
private static DefinitionsProvider DEFAULTS = new DefinitionsProvider() {
#Override
public void applyDefinitions(Configuration config) {
config.set(CoapConfig.MAX_RESOURCE_BODY_SIZE, DEFAULT_MAX_RESOURCE_SIZE);
config.set(CoapConfig.MAX_MESSAGE_SIZE, DEFAULT_BLOCK_SIZE);
config.set(CoapConfig.PREFERRED_BLOCK_SIZE, DEFAULT_BLOCK_SIZE);
}
};
private static class AsynchListener implements CoapHandler {
#Override
public void onLoad(CoapResponse response) {
System.out.println( response.getResponseText() );
}
#Override
public void onError() {
System.err.println("Error");
}
}
/*
* Application entry point.
*/
public static void main(String args[]) {
Configuration config = Configuration.createWithFile(CONFIG_FILE, CONFIG_HEADER, DEFAULTS);
Configuration.setStandard(config);
URI uri = null; // URI parameter of the request
if (args.length > 0) {
// input URI from command line arguments
try {
uri = new URI(args[0]);
} catch (URISyntaxException e) {
System.err.println("Invalid URI: " + e.getMessage());
System.exit(-1);
}
CoapClient client = new CoapClient(uri);
client.useNONs();
// observe
AsynchListener asynchListener = new AsynchListener();
CoapObserveRelation observation = client.observe(asynchListener);
// User presses ENTER to exit
System.out.println("Press ENTER to exit...");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try { br.readLine(); } catch (IOException e) { }
System.out.println("Exiting...");
observation.proactiveCancel();
}
So i'm controlling the FPS by sending PUT requests with a server that has a counter 0-50.
Not sure, what your doing.
That seems to be wired and not related to RFC7252 nor RFC7641.
CoAP is designed for REST, I don't see any benefit in using it for video streaming.
Using Eclipse/Californium on a Intel n6005 with 16GB RAM, the CoAP/DTLS server runs on about 60000 requests/second. The benchmark uses 2000 clients in parallel.
See also Eclipse/Californium - Benchmarks j5005
Using only one client with CON requests, the performance is mainly limited by the RTT. 30 requests/second should work, if that RTT is accordingly small.
Using NON requests doesn't really help. CoAP RFC7252 defines two layers, a messaging layer and an application layer. NON affects only the messaging layer, but a NON request will wait for it's response, if NSTART-1 should be used.
If your RTT is the issue, you may try to escape that either using requests with "No Server Response" (RFC7967) or multiple NON responses (RFC7641). The first is not intended for fast requests, the second is more a work-around of the initial statement, that CoAP is REST not video-streaming.
So, what is your RTT?
I am trying to implement GRPC and when i do so I get the correct response from the server and if I stop the server and run it again and use the other request that I implemented it works however if I try and make a second request straight after making one in from the first request I get the same response. It's like it is looping.
These are the two methods I am using from the client:
public void setSpaces(int id) {
channel =ManagedChannelBuilder.forAddress("localhost", 3000)
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
// needing certificates.
.usePlaintext()
.build();
blockingStub = carParkServiceGrpc.newBlockingStub(channel);
asyncStub = carParkServiceGrpc.newStub(channel);
logger.info("Will try to get CarPark " + id + " ...");
CarParkToUpdateRequest request = CarParkToUpdateRequest.newBuilder().setDeviceId(id).build();
carParkResponse response;
try {
response = blockingStub.setSpaces(request);
}catch(StatusRuntimeException e) {
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
return;
}finally {
channel.shutdown();
}
logger.info("Carpark: " + response.getCarPark());
spacesArea.append(response.getCarPark().toString());
}
public void setFull(int id) {
channel =ManagedChannelBuilder.forAddress("localhost", 3000)
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
// needing certificates.
.usePlaintext()
.build();
blockingStub = carParkServiceGrpc.newBlockingStub(channel);
asyncStub = carParkServiceGrpc.newStub(channel);
logger.info("Will try to get CarPark " + id + " ...");
CarParkToUpdateRequest request = CarParkToUpdateRequest.newBuilder().setDeviceId(id).build();
carParkResponse response;
try {
response = blockingStub.setFull(request);
}catch(StatusRuntimeException e) {
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
return;
}finally {
channel.shutdown();
}
logger.info("Carpark: " + response.getCarPark());
fullArea.append(response.getCarPark().toString());
}
These two methods are supposed to send a request to the server to change the status of the 'car park' so if I send a request with setFull I get a response saying the carpark is full etc.
These are the methods from the server:
public void setSpaces(CarParkToUpdateRequest request, StreamObserver<carParkResponse> rStreamObserver) {
ArrayList<CarParkOperations.proto.cp.CarPark> carList = Car.getInstance();
for(int i=0; i<carList.size(); i++) {
if(carList.get(i).getCarParkId() == request.getDeviceId()) {
CarParkOperations.proto.cp.CarPark heater_rec = (CarParkOperations.proto.cp.CarPark) carList.get(i);
Car.carparkCar.clear();
Car.carparkCar.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(heater_rec.getCarParkId()).setLocation(heater_rec.getLocation()).setStatus("Spaces").build());
}
}
for(CarParkOperations.proto.cp.CarPark heater : Car.carparkCar) {
carParkResponse response = carParkResponse.newBuilder().setCarPark(heater).build();
rStreamObserver.onNext(response);
rStreamObserver.onCompleted();
return;
}
}
public void setFull(CarParkToUpdateRequest request, StreamObserver<carParkResponse> rStreamObserver) {
ArrayList<CarParkOperations.proto.cp.CarPark> carList = Car.getInstance();
for(int i=0; i<carList.size(); i++) {
if(carList.get(i).getCarParkId() == request.getDeviceId()) {
CarParkOperations.proto.cp.CarPark heater_rec = (CarParkOperations.proto.cp.CarPark) carList.get(i);
Car.carparkCar.clear();
Car.carparkCar.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(heater_rec.getCarParkId()).setLocation(heater_rec.getLocation()).setStatus("Full").build());
}
}
for(CarParkOperations.proto.cp.CarPark heater : Car.carparkCar) {
carParkResponse response = carParkResponse.newBuilder().setCarPark(heater).build();
rStreamObserver.onNext(response);
rStreamObserver.onCompleted();
return;
}
}
I think it's most likely something to do with the server methods but cant seem to figure it out.
This is where I am storing the data:
package CarParkOperations.proto.cp;
import java.util.ArrayList;
import com.google.rpc.Status;
public class Car extends ArrayList<CarPark>{
public static Car carparkCar;
public static Car getInstance() {
if(carparkCar == null) {
carparkCar = new Car();
}
return carparkCar;
}
public Car() {
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(1).setStatus("Full").setLocation("Behind Building 1").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(2).setStatus("Full").setLocation("Behind Building 1").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(3).setStatus("Full").setLocation("Behind Building 4").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(4).setStatus("Full").setLocation("Behind Building 3").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(5).setStatus("Full").setLocation("Behind Building 2").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(6).setStatus("Full").setLocation("Behind Building 1").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(7).setStatus("Full").setLocation("Behind Building 1").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(10).setStatus("Full").setLocation("Behind Building 6").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(11).setStatus("Full").setLocation("Behind Building 1").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(12).setStatus("Spaces").setLocation("Behind Building 1").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(13).setStatus("Spaces").setLocation("Behind Building 1").build());
this.add(CarParkOperations.proto.cp.CarPark.newBuilder().setCarParkId(14).setStatus("Spaces").setLocation("Behind Building 1").build());
}
}
Any suggestions would be much appreciated.
You might need synchronize Car.getInstance() method, because without proper synchronization, if it is called by different threads it may surprisingly return different instances!
public static synchronized Car getInstance() {
if(carparkCar == null) {
carparkCar = new Car();
}
return carparkCar;
}
Also your Car class is not thread-safe because it extends ArrayList which is not thread-safe. You should let your Car class extend something like ConcurrentLinkedQueue instead, or let your Car class compose a field of list = Collections.synchronizedList(new ArrayList()) instead of extending ArrayList.
I am doing a project MAS project on jade.
I have 2 agents one to send and one to receive.
How do i make agent 1 send a message to agent 2, for example "1000", and only when agent 2 receives the "1000", agent 2 will reply with for example, "turn off"?
My sugestion is to create agent1 with a RequestPerformer behaviour and agent2 with a CyclicBehaviour to listen messages.
agent1 behaviour content can be like this:
ACLMessage cfp = new ACLMessage(ACLMessage.CFP);
cfp.addReceiver(/*agent2AID*/);
cfp.setContent("1000");
cfp.setConversationId(1000);
cfp.setReplyWith("cfp" + System.currentTimeMillis()); // Unique value
myAgent.send(cfp);
mt = MessageTemplate.and(MessageTemplate.MatchConversationId(targetProduct),
MessageTemplate.MatchInReplyTo(cfp.getReplyWith()));
agent2 behaviour can be like this:
private class CFPServer extends CyclicBehaviour {
private static final long serialVersionUID = 1L;
public void action() {
MessageTemplate mt = MessageTemplate.MatchPerformative(ACLMessage.CFP);
ACLMessage msg = myAgent.receive(mt);
if (msg != null) {
// CFP Message received. Process it
String title = msg.getContent();
ACLMessage reply = msg.createReply();
// The requested fruit is NOT available for sale.
reply.setPerformative(ACLMessage.INFORM);
reply.setContent("turn off");
myAgent.send(reply);
} else {
block();
}
}
}
The related problem is very well discussed and solved in many ways, you can check out the following implementation of a simple Contract Net Protocol where you can find send and reply functions:
https://github.com/clebercbr/tp_cnp/blob/master/src/java/
See that the agent initiator is like agent1 and participant and rejector are like your agent2
I do not recommend to write a cyclic behaviour for messageReception, it will burn the proc for nothing. A simpleBehaviour with done() at false and a block() in the action() is much more efficient.
Regarding your pb, something like that should work :
public class ReceiveMessageBehaviour extends SimpleBehaviour{
private boolean finished=false;
/**
*
* This behaviour is a one Shot.
* It receives a message tagged with an inform performative, print the content in the console and destroy itself if its equal to 1000
* #param myagent
*/
public ReceiveMessageBehaviour(final Agent myagent) {
super(myagent);
}
public void action() {
//1) receive the message
final MessageTemplate msg Template = MessageTemplate.MatchPerformative(ACLMessage.INFORM);
final ACLMessage msg = this.myAgent.receive(msgTemplate);
//2) check its caracts
if (msg != null && msg.getContent().equals("1000")) {
System.out.println(this.myAgent.getLocalName()+"<----Result received from "+msg.getSender().getLocalName()+" ,content= "+msg.getContent());
this.finished=true;
//3) answer
final ACLMessage msg2 = new ACLMessage(ACLMessage.INFORM);
msg2.setSender(this.myAgent.getAID());
msg2.addReceiver(new AID(msg.getSender().getLocalName(), AID.ISLOCALNAME));
msg2.setContent("turn off");
this.myAgent.send(msg2);
}else{
block();// the behaviour goes to sleep until the arrival of a new message in the agent's Inbox.
}
}
public boolean done() { return finished;}
}
I'm new to these JADE related components. But I would like to add the simplest code that we can use to solve this problem using oneShotBehaviour and Cyclicehaviour.
The following code enables simple communication among two clients.
Agent A-Class:-
public class Agent_A extends Agent{
protected void setup(){
System.out.println(getAID().getName()+" is ready.");
addBehaviour(new AgentA_SendMessage());
addBehaviour(new AgentA_ReceiveMessage());
}
public class AgentA_SendMessage extends OneShotBehaviour{
#Override
public void action(){
ACLMessage msg = new ACLMessage(ACLMessage.REQUEST);
msg.addReceiver(new AID("AgentB",AID.ISLOCALNAME));
msg.setContent("1000");
send(msg);
}
}
public class AgentA_ReceiveMessage extends CyclicBehaviour{
Scanner scn2 = new Scanner(System.in);
#Override
public void action(){
ACLMessage remsg = myAgent.receive();
if(remsg!=null){
String reply = remsg.getContent();
System.out.println("Reply From "+remsg.getSender()+" :- "+reply);
}
else{
block();
}
}
}
}
Agent B Class:-
public class Agent_B extends Agent{
protected void setup(){
System.out.println("Hello Seller Agent : "+getAID().getName()+" is ready.");
addBehaviour(new AgentB_ReceiveMessage());
}
public class AgentB_ReceiveMessage extends CyclicBehaviour{
#Override
public void action(){
ACLMessage remsg = myAgent.receive();
if(remsg!=null){
System.out.println(""+remsg);
ACLMessage reply = remsg.createReply();
reply.setPerformative(ACLMessage.INFORM);
String price = remsg.getContent();
reply.setContent("off");
send(reply);
}else{
block();
}
}
}
}
Play 2.5 Highlights states
Better control over WebSocket frames
The Play 2.5 WebSocket API gives you direct control over WebSocket frames. You can now send and receive binary, text, ping, pong and close frames. If you don’t want to worry about this level of detail, Play will still automatically convert your JSON or XML data into the right kind of frame.
However
https://www.playframework.com/documentation/2.5.x/JavaWebSockets has examples around LegacyWebSocket which is deprecated
What is the recommended API/pattern for Java WebSockets? Is using
LegacyWebSocket the only option for java websockets?
Are there any examples using new Message types ping/pong to implement a heartbeat?
The official documentation on this is disappointingly very sparse. Perhaps in Play 2.6 we'll see an update to this. However, I will provide an example below on how to configure a chat websocket in Play 2.5, just to help out those in need.
Setup
AController.java
#Inject
private Materializer materializer;
private ActorRef chatSocketRouter;
#Inject
public AController(#Named("chatSocketRouter") ActorRef chatInjectedActor) {
this.chatSocketRouter = chatInjectedActor;
}
// Make a chat websocket for a user
public WebSocket chatSocket() {
return WebSocket.Json.acceptOrResult(request -> {
String authToken = getAuthToken();
// Checking of token
if (authToken == null) {
return forbiddenResult("No [authToken] supplied.");
}
// Could we find the token in the database?
final AuthToken token = AuthToken.findByToken(authToken);
if (token == null) {
return forbiddenResult("Could not find [authToken] in DB. Login again.");
}
User user = token.getUser();
if (user == null) {
return forbiddenResult("You are not logged in to view this stream.");
}
Long userId = user.getId();
// Create a function to be run when we initialise a flow.
// A flow basically links actors together.
AbstractFunction1<ActorRef, Props> getWebSocketActor = new AbstractFunction1<ActorRef, Props>() {
#Override
public Props apply(ActorRef connectionProperties) {
// We use the ActorRef provided in the param above to make some properties.
// An ActorRef is a fancy word for thread reference.
// The WebSocketActor manages the web socket connection for one user.
// WebSocketActor.props() means "make one thread (from the WebSocketActor) and return the properties on how to reference it".
// The resulting Props basically state how to construct that thread.
Props properties = ChatSocketActor.props(connectionProperties, chatSocketRouter, userId);
// We can have many connections per user. So we need many ActorRefs (threads) per user. As you can see from the code below, we do exactly that. We have an object called
// chatSocketRouter which holds a Map of userIds -> connectionsThreads and we "tell"
// it a lightweight object (UserMessage) that is made up of this connecting user's ID and the connection.
// As stated above, Props are basically a way of describing an Actor, or dumbed-down, a thread.
// In this line, we are using the Props above to
// reference the ActorRef we've just created above
ActorRef anotherUserDevice = actorSystem.actorOf(properties);
// Create a lightweight object...
UserMessage routeThisUser = new UserMessage(userId, anotherUserDevice);
// ... to tell the thread that has our Map that we have a new connection
// from a user.
chatSocketRouter.tell(routeThisUser, ActorRef.noSender());
// We return the properties to the thread that will be managing this user's connection
return properties;
}
};
final Flow<JsonNode, JsonNode, ?> jsonNodeFlow =
ActorFlow.<JsonNode, JsonNode>actorRef(getWebSocketActor,
100,
OverflowStrategy.dropTail(),
actorSystem,
materializer).asJava();
final F.Either<Result, Flow<JsonNode, JsonNode, ?>> right = F.Either.Right(jsonNodeFlow);
return CompletableFuture.completedFuture(right);
});
}
// Return this whenever we want to reject a
// user from connecting to a websocket
private CompletionStage<F.Either<Result, Flow<JsonNode, JsonNode, ?>>> forbiddenResult(String msg) {
final Result forbidden = Results.forbidden(msg);
final F.Either<Result, Flow<JsonNode, JsonNode, ?>> left = F.Either.Left(forbidden);
return CompletableFuture.completedFuture(left);
}
ChatSocketActor.java
public class ChatSocketActor extends UntypedActor {
private final ActorRef out;
private final Long userId;
private ActorRef chatSocketRouter;
public ChatSocketActor(ActorRef out, ActorRef chatSocketRouter, Long userId) {
this.out = out;
this.userId = userId;
this.chatSocketRouter = chatSocketRouter;
}
public static Props props(ActorRef out, ActorRef chatSocketRouter, Long userId) {
return Props.create(ChatSocketActor.class, out, chatSocketRouter, userId);
}
// Add methods here handling each chat connection...
}
ChatSocketRouter.java
public class ChatSocketRouter extends UntypedActor {
public ChatSocketRouter() {}
// Stores userIds to websockets
private final HashMap<Long, List<ActorRef>> senders = new HashMap<>();
private void addSender(Long userId, ActorRef actorRef){
if (senders.containsKey(userId)) {
final List<ActorRef> actors = senders.get(userId);
actors.add(actorRef);
senders.replace(userId, actors);
} else {
List<ActorRef> l = new ArrayList<>();
l.add(actorRef);
senders.put(userId, l);
}
}
private void removeSender(ActorRef actorRef){
for (List<ActorRef> refs : senders.values()) {
refs.remove(actorRef);
}
}
#Override
public void onReceive(Object message) throws Exception {
ActorRef sender = getSender();
// Handle messages sent to this 'router' here
if (message instanceof UserMessage) {
UserMessage userMessage = (UserMessage) message;
addSender(userMessage.userId, userMessage.actorRef);
// Watch sender so we can detect when they die.
getContext().watch(sender);
} else if (message instanceof Terminated) {
// One of our watched senders has died.
removeSender(sender);
} else {
unhandled(message);
}
}
}
Example
Now whenever you want to send a client with a websocket connection a message you can do something like:
ChatSenderController.java
private ActorRef chatSocketRouter;
#Inject
public ChatSenderController(#Named("chatSocketRouter") ActorRef chatInjectedActor) {
this.chatSocketRouter = chatInjectedActor;
}
public static void sendMessage(Long sendToId) {
// E.g. send the chat router a message that says hi
chatSocketRouter.tell(new Message(sendToId, "Hi"));
}
ChatSocketRouter.java
#Override
public void onReceive(Object message) throws Exception {
// ...
if (message instanceof Message) {
Message messageToSend = (Message) message;
// Loop through the list above and send the message to
// each connection. For example...
for (ActorRef wsConnection : senders.get(messageToSend.getSendToId())) {
// Send "Hi" to each of the other client's
// connected sessions
wsConnection.tell(messageToSend.getMessage());
}
}
// ...
}
Again, I wrote the above to help out those in need. After scouring the web I could not find a reasonable and simple example. There is an open issue for this exact topic. There are also some examples online but none of them were easy to follow. Akka has some great documentation but mixing it in with Play was a tough mental task.
Please help improve this answer if you see anything that is amiss.
I`m using CometD-3.0.2 in Java multi-threading application following this tutorial. Everything works fine when I create a service channel. I have created an object of my Socket Connection class, that also starts 10 Java Threads, in the CometD initialise class
public class Initializer extends GenericServlet
{
private MainConnect connect;
#Override
public void init() throws ServletException
{
connect = new MainConnect();
// Retrieve the CometD service instantiated by AnnotationCometdServlet
StockPriceService service = (StockPriceService)getServletContext().getAttribute(StockPriceService.class.getName());
// Register the service as a listener of the emitter
connect.getListeners().add(service);
System.out.println("Service Added!");
//Initiate the Threads
connect.InitiateConnection();
}
My Socket class gets initiated and java threads are running as well. However, the jetty server never get started here.
My Service class
#Service
public class StockPriceService implements StockPriceEmitter.Listener
{
#Inject
private BayeuxServer bayeuxServer;
#Session
private LocalSession sender;
public void onUpdates(List<StockPriceEmitter.Update> updates)
{
for (StockPriceEmitter.Update update : updates)
{
// Create the channel name using the stock symbol
String channelName = "/stock/" + update.getSymbol().toLowerCase(Locale.ENGLISH);
// Initialize the channel, making it persistent and lazy
bayeuxServer.createIfAbsent(channelName, new ConfigurableServerChannel.Initializer()
{
public void configureChannel(ConfigurableServerChannel channel)
{
channel.setPersistent(true);
channel.setLazy(true);
}
});
// Convert the Update business object to a CometD-friendly format
Map<String, Object> data = new HashMap<String, Object>(4);
data.put("symbol", update.getSymbol());
data.put("oldValue", update.getOldValue());
data.put("newValue", update.getNewValue());
// Publish to all subscribers
ServerChannel channel = bayeuxServer.getChannel(channelName);
channel.publish(sender, data, null);
System.out.println("Service hit!!");
}
}
}
Java multi-thread run method
if (workerID == 0) {
try {
System.out.println("Running");
Random random = new Random();
List<Update> updates = new ArrayList<Update>();
// Randomly choose how many stocks to update
int howMany = random.nextInt(symbols.size()) + 1;
for (int i = 0; i < howMany; ++i)
{
// Randomly choose which one to update
int which = random.nextInt(symbols.size());
String symbol = symbols.get(1);
float oldValue = values.get(symbol);
// Randomly choose how much to update
boolean sign = random.nextBoolean();
float howMuch = random.nextFloat();
float newValue = oldValue + (sign ? howMuch : -howMuch);
// Store the new value
values.put(symbol, newValue);
updates.add(new Update(symbol, oldValue, newValue));
// System.out.println("Updates from Emitter Class: " + updates);
}
// Notify the listeners
for (Listener listener : MainConnect.getListeners())
{
System.out.println("Listners value: " + listener);
listener.onUpdates(updates);
}
// Randomly choose how long for the next update
// We use a max delay of 1 second to simulate a high rate of updates
long howLong = random.nextInt(1000);
scheduler.schedule(this, howLong, TimeUnit.MILLISECONDS);
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Overall scenario, Im implementing a Sever that uses Sockets to receive client A connection then receive data from clients. I need this data to stream to client B. Jetty server doesnt start when I initiate the Socket class/ open a socket connection.
Any information is much appreciated