In my app (streaming series, movies) I have a section for users that can set Reminder for the series or movies. And I implement Pusher to receive server message for reminding data.
Is it true that I connect to channel for each item in the reminder list?? or I should connect to the pusher once and in the pusher event get related series/ movies message?(Server-side implemented pusher for each reminder list items, should we change server-side implementation or I can connect to pusher for each items? )
This is my Implementation for pusher:
public Pusher getPusher() throws Exception {
if (pusher == null) {
HttpAuthorizer auth = new HttpAuthorizer(BuildConfig.PUSHER);
HashMap<String, String> authHeader = new HashMap<>();
authHeader.put("Authorization", SharedPref.INSTANCE.read(AUTH_TOKEN, ""));
auth.setHeaders(authHeader);
PusherOptions option = new PusherOptions();
option.setCluster(BuildConfig.PUSHER_CLUSTER);
option.setAuthorizer(auth);
pusher = new Pusher(BuildConfig.PUSHER_KEY, option);
pusher.subscribePrivate("private-app_ch." + serialId, new PrivateChannelEventListener() {
#Override
public void onAuthenticationFailure(String s, Exception e) {
Timber.i("pusher onAuthenticationFailure " + e.getMessage());
}
#Override
public void onSubscriptionSucceeded(String s) {
Timber.i("pusher onSubscriptionSucceeded: " + s);
}
#Override
public void onEvent(String s, String s1, String result) {
Timber.i("pusher onEvent" + s + ":" + s1);
Timber.i("pusher onEvent" + result);
}
}, "App\\Events\\AppBroadcastEvent");
}
return pusher;
}
The best practice for this would be to maintain one connection to Channels but make a subscription for each item in the reminder list.
So you would call pusher.subscribePrivate for each item in the reminder list and then on the server side publish to each individual Channel when a reminder needs to be sent.
For example if a user wanted to be reminded about 'Stranger Things' and 'Orange is the new black' you would subscribe to both:
pusher.subscribePrivate("private-app_ch.strangerthings"
and
pusher.subscribePrivate("private-app_ch.orangeisthenewblack"
Your server would then publish reminders about 'Stranger Things' to the Stranger things channel and OISTNB to the OISTNB channel and so on.
This way only relevant updates are sent to the client (server-side filtering). If you only subscribe to one channel the client will get messages they may not want updates about and you would have to filter these out on the client side.
This is also explained here: https://support.pusher.com/hc/en-us/articles/360025398514-Should-i-subscribe-to-lots-of-Channels-
One additional point that is worth considering is that Channels will only maintain an active connection when the app is open. The connection will be closed when the app is backgrounded/closed. This means for reminders to be sent the user would always have to be in your app. You may want to consider also sending push notifications when the app is closed so the user does not miss reminders.
Related
I am using gprc bidirectional streaming for chat application in Spring boot and since StreamObserver<T> object is used to send message back from sever to client. So, I want to serialize StreamObserver<T> object and convert it into stream of bytes to store it in redis or some other database. But since, StreamObserver<T> is a interface which doesn't implement or extend serializable. So, I am looking for a solution to how to serialize it since there would be around thousands of user which be using the chat application and storing StreamObserver <T> in some Map<String, StreamObserver<T>> won't be good idea.
Currently, I am storing StreamObserver<T> objects in map.
Map<String, StreamObserver<T>>
Here, key of map is chat application's user's id and value of is StreamObserver object which contains onNext, onError, onCompleted functions to send message from server to client
// Storing StreamObserver object with user Id
public static Map<String, StreamObserver<Chat.ChatMessageFromServer>> observersMap = new HashMap<String, StreamObserver<Chat.ChatMessageFromServer>>();
#Override
public StreamObserver<Chat.ChatMessage> chat(final StreamObserver<Chat.ChatMessageFromServer> responseObserver) {
// responseObserver -> Storing it into a map. So, server could send message back to the client
String user = grpcServerInterceptor.contextKey.get();
System.out.println("");
System.out.println("User : " + user);
if (observersMap.get(user) == null) {
System.out.println("New User : " + user);
System.out.println("Adding User to observers map");
System.out.println("");
observersMap.put(user, responseObserver);
} else {
System.out.println("This User already exists in observersMap : " + user);
System.out.println("By the way, Updating it");
observersMap.put(user, responseObserver);
}
// This function sends message to client from Server
public void sendMessageFromServerToClient(String user, String message) {
// Fetching StreamObserver from observersMap as defined above
observersMap.get(user).onNext(Chat.ChatMessageFromServer.newBuilder().setMessage(Chat.ChatMessage.newBuilder().setTo(user).setFrom("Server").setMessage(message)).build());
System.out.println("Pushed message to user : " + user);
System.out.println("");
}
StreamObserver corresponds to a stream on a real TCP connection. That resource can't be transferred to a DB. There's no way to serialize it to a DB to reduce memory usage.
I have been building my GWT app, using JSONP to communicate with a push server (my own code, based on Netty library). The communication function of the GWT app looks like the following, which sends queries stored in in queryList and processes received data from server via function processDataFromServer. You may notice that right after a fail or a success of communicating, the function calls itself again for keeping connection with server:
ArrayList<String>queryList;
boolean querying = false;
public void queryJsonpServer() {
if (querying) {
return;
}
querying = true;
String jsonString = queryList.isEmpty() ? “” : queryList.remove(0);
String url = postUrl + (!jsonString.isEmpty() ? “?jsonp=" + URL.encodeQueryString(jsonString) : "");
JsonpRequestBuilder jsonp = new JsonpRequestBuilder();
jsonp.setTimeout(60 * 1000);
jsonp.requestObject(url, new AsyncCallback<MyJsonpObject>() {
#Override
public void onFailure(Throwable caught) {
querying = false;
queryJsonpServer();
}
#Override
public void onSuccess(MyJsonpObject o) {
processDataFromServer(o);
querying = false;
queryJsonpServer();
}
});
}
The code works fine if the communication are successes (onSuccess called).
However, once it fails (onFailure called, because of timeout for example), even the function (queryJsonpServer) is called again (I am sure a new query is sent, server receives that query and sends back new data), the function is stuck to receive that new data (onSuccess has not been called since that fail). After a while onFailure called again because of timeout. The problem repeats: query via onFailure, receive nothing, onFailure called again...
Anyone has idea about that problem? Thanks
My android app is using a http connection to send data to a server. If the server received the data the app marks the data as successfully transmitted in its own database.
Sometimes external problems may occur, so that the transmission cannot be completed. The app should try sending the data again later.
What is a good possibility to make sure, that the data reaches the server? I only can think of a service, which checks periodically for not transmitted data, but I don't like this approach.
Heres some psuedo code..
1.Create a AcknowledgementManager which waits for acknowledgement of each request.
2.The acknowledgement manager posts a runnable which will run after TIME_OUT interval.
public void startListeningForTimeOut(CallContext callContext) {
TimeOutRunnable timeOutRunnable = new TimeOutRunnable(callContext);
mMapRunnables.put(callContext, timeOutRunnable);
mHandler.postDelayed(timeOutRunnable, TIMEOUT_DURATION);
Slog.d(TAG,
"started listening for timeout for token: " + callContext + " at: "
+ System.currentTimeMillis());
}
3.1 If the AcknowledgementManager receives acknowledgement for the data it cancels the runnable for that data and make necessary updates in database.
public void stopListeningForTimeOut(CallContext callContext) {
mHandler.removeCallbacks(mMapRunnables.get(callContext));
mMapRunnables.remove(callContext);
Slog.d(TAG,
"stopped listening for timeout for token: " + callContext + " at: "
+ System.currentTimeMillis());
}
3.2 If the acknowledgement is not received the runnable raises a "timeout" to the AcknowledgementManager.The AcknowledgementManager retries sending data.
public class TimeOutRunnable implements Runnable {
private static final String TAG = "TimeOutThread rupesh";
CallContext mToken;
// String mCallback;
public TimeOutRunnable(CallContext callContext) {
mToken = callContext;
// mCallback = callbackTBD;
// FIXME send proper callback class
}
#Override
public void run() {
Slog.d(TAG, "timeout occured for data id: " + mToken + " at: " + System.currentTimeMillis());
mToken.onTimeOutOccurred();
}
}
Please note:
1.Before syncing the data please make the entry of data in db so that the data does not get lost between app restart.
2.Before you start the syncing process query the db to get the data which is not being transmitted.Based on the data size,make sure you keep the data to be transmitted in memory to avoid hitting the db everytime.
3.Once you get acknowledgement for a data item,make necessary changes for that entry in db.
4.You can also have 'retry' flag in db so that you keep track of no of retries.
I'm using smack to send a chat message. While testing i figured out that, when the network is unavilable, the API does not throw any exceptions.
my code :
Chat chat = connection.getChatManager().createChat(
"abc#gmail.com", new MessageListener() {
#Override
public void processMessage(Chat arg0, Message arg1) {
System.out.println(arg1.getFrom() + " says " + arg1.getBody());
}
});
// I Put a break point here and deliberately disable the network.
// But the following line is not throwing the XMPPException
chat.sendMessage("smack says hi.."); /* Send the message  */
Should i add any listeners to capture the exception?
You won't get an exception since it is an asynchronous call. If you want to know if the connection has been dropped, you will have to register a ConnectionListener with the Connection.
I'm currently using Topic based communication using JADE. I'm able to register a JADE agent using jade.core.messaging.TopicManagementFEService thereby connecting to the main-container in the same platform.
The details are below:
Main-Container: a simple LAMP/WAMP Server that hosts the Main-Container.
Client: An Android Emulator(testing purpose) to connect to the main-container.
Currently,
Server starts the main-container
Android emulator connects to the Main-container successfully (Agent created along with Topic Mgmt Service enabled)
Server is sending messages based on a specific topic.
But my Android Client is not able to receive this message although the topic registered is the same on both ends!
You can see the code below:
Server Side:
TopicManagementHelper topicHelper = (TopicManagementHelper) getHelper(TopicManagementHelper.SERVICE_NAME);
final AID sensorTopic = topicHelper.createTopic("JADE");
topicHelper.register(sensorTopic);
addBehaviour(new TickerBehaviour(this, TIMER_VALUE_IN_MILLISECONDS) {
private static final long serialVersionUID = -2567778187494378326L;
public void onTick() {
ACLMessage msg = new ACLMessage(ACLMessage.INFORM);
msg.addReceiver(eventTopic);
msg.setContent(eventValue);
myAgent.send(msg);
}
});
Android Side:
// Registering on Android Side as well
TopicManagementHelper topicHelper = (TopicManagementHelper) getHelper(TopicManagementHelper.SERVICE_NAME);
topic = topicHelper.createTopic("JADE"); // See, same topic!
topicHelper.register(topic);
behaviour = new myBehaviour(this, TIMER_VALUE_IN_MILLISECONDS, topic);
addBehaviour(behaviour);
private class myBehaviour extends TickerBehaviour {
private static final long serialVersionUID = 4782913834042415090L;
AID topic;
Agent agent;
MessageTemplate tpl;
public myBehaviour(Agent a, long period, AID topic) {
super(a, period);
this.agent = a;
this.topic = topic;
}
public void onTick() {
tpl = MessageTemplate.MatchTopic(topic);
ACLMessage msg = receive(tpl);
if (msg != null) {
logger.log(Level.INFO, "Agent "+ agent.getLocalName() +
": Message about topic "+ topic.getLocalName() +" received. \n" +
"Content is " + msg.getContent());
data = msg.getContent();
} else {
logger.log(Level.INFO, "In here..."); // Always executes only this code!
block();
}
}
}
Where am I going wrong here? It always executes the else part in the Android side which is obvious to say that message received is NULL!
Never mind. The logic was wrong. The Android-Agent was not identifying itself to the Central-Agent.
I set the Ontology so that the Central Agent is able to identify such message and sends the message accordingly. Now, it is receiving messages!
Self-help works sometimes! ;-)
Receiving topic messages doesn't work correctly with Android up to version 4.3.0 in JADE. Android can send out topic messages but can't receive them. I found this out through my own issues. I've posted more info about it in my own question on stack overflow.
Take a look. JADE Leap Android App unable to receive topic messages