I am using processing (IE, JAVA) to publish via pubnub. After reading nearly all the simple startup documentation, I am at a loss. My code is running, but not publishing anything. I have the libraries installed correctly I believe, but no messages are published via my keys.
I know I am just not understanding something really simple here, but I have no idea what that might be. I added a 5s delay to ensure everything finishes before setup stops as suggested in other posts. Right now it runs, but no messages show up in the app with those keys(which I replaced below). All the examples are way over my head and not helping my poor noob brain.
I also setup the same basic code in Python which also runs but does not actually publish the message. Leading me to think I am just missing something very fundamental.
In the end, I want to push data from a UI to an online real-time dashboard. This seems trivially easy, but it is a complete black hole.
Here is my code (it's basically just a copy and paste from the startup code):
import com.pubnub.api.*;
import org.json.*;
Pubnub pubnub = new Pubnub("PUBKEY", "SUBKEY");
void setup()
{
try {
pubnub.subscribe("NSFPhaseII", new Callback() {
#Override
public void connectCallback(String channel, Object message) {
pubnub.publish("NSFPhaseII", "Hello from the PubNub Java SDK", new Callback() {});
}
#Override
public void disconnectCallback(String channel, Object message) {
System.out.println("SUBSCRIBE : DISCONNECT on channel:" + channel
+ " : " + message.getClass() + " : "
+ message.toString());
}
public void reconnectCallback(String channel, Object message) {
System.out.println("SUBSCRIBE : RECONNECT on channel:" + channel
+ " : " + message.getClass() + " : "
+ message.toString());
}
#Override
public void successCallback(String channel, Object message) {
System.out.println("SUBSCRIBE : " + channel + " : "
+ message.getClass() + " : " + message.toString());
}
#Override
public void errorCallback(String channel, PubnubError error) {
System.out.println("SUBSCRIBE : ERROR on channel " + channel
+ " : " + error.toString());
}
});
}
catch (PubnubException e) {
System.out.println(e.toString());
}
delay(5000);
println("done");
}
I am answering my own question here.
It turns out this code has been working all along. I setup two processing sketches in a publish subscribe type setup and could clearly see that one was receiving the publishes from the other.
Then, when I checked on pubnub, the messages registered in my account. I am not really sure if the messages were registering in my account all along and just not updating or if they took some time, but I believe it was working all along and I was just not waiting long enough to see the messages register online.
Like I said. Noobs...
Related
I am trying to accomplish an Unity game demo with network function, using C# for programming of client, and Java for server.
To be specific, server communication is implemented by Netty.
I also brought in Protobuf, which helps me define protocols of messages.
As I am new to server programming, dealing with packet merging and loss in TCP has not been considered in my code yet.
When I created sockets from client, and sent message to server, everything went well.
Problem happened when server replied:
In the client, an async method is ready to receive message. When I simply sent a string-format message from server, the method were able to get it.
But when I replaced the message with a 4-length byte[], which encoded from a Protobuf Message object, client just showed that it received NOTHING.
when I print what I've sent in the server console, it is like this:
00001000
00000001
00010000
00000001
My server code overrides channelRead and channelReadComplete functions of Netty.
In channelRead, ChannelHandlerContext.write was invoked to write the message to the transmission cache.
And in channelReadComplete, ChannelHandlerContext.flush was invoked, so that the message could be sent finally.
channelRead()
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
Request.MsgPack msgPack = (Request.MsgPack) msg;
Request.MsgPack.MsgType type = msgPack.getType();
switch (type)
{
case GetServerState:
final Request.GetServerState gssbody = msgPack.getGetServerState();
System.out.println("收到类型为" + type + "的消息,内容为:" +
"\nrequestId = " + gssbody.getRequestId()
);
byte[] bytes = ServerStateManager.getState(gssbody.getRequestId());
ctx.write(bytes);
break;
getState(): including Protobuf-encoding procedure
public static byte[] getState(int requestId)
{
ReturnServerState.Message.Builder replyBuilder = ReturnServerState.Message.newBuilder();
replyBuilder.setRequestId(requestId);
replyBuilder.setIsIdle(new ServerStateManager().isIdle());
return replyBuilder.build().toByteArray();
}
channelReadComplete()
#Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
try
{
ctx.flush();
}
finally
{
ctx.close();
}
}
Client code:
public class ShortLink
{
Socket clientSocket = null;
static byte[] result = new byte[1024];
Task ReceiveAsync<T>(string ip, int port)
{
return Task.Run(() =>
{
T component = default(T);
while (clientSocket.Receive(result) == 0)
{
break;
ReceiveAsync is invoked in the way of:
await ReceiveAsync<ReturnServerState>(ip, port);
when I found clientSocket.Receive(result) always output 0, I tried to log result[0], result[1], result[2], result[3] like this:
Debug.Log(Convert.ToString(result[0]) + ", " +
Convert.ToString(result[1]) + ", " +
Convert.ToString(result[2]) + ", " +
Convert.ToString(result[3]));
And the log turned to be 0,0,0,0.
I will be grateful for any idea of "why the client socket received nothing", and the solution.
Since I come from Asia, there may be a time lag between your reply and mine, and also English is not my mother tongue. However, I will try my best to reply in time.
Thanks a lot!
Okay..I have finally solve it myself
1.The usage "return replyBuilder.build().toByteArray()" is wrong because ProtoEncoder has already do toByteArray() for me:
public class ProtobufEncoder extends MessageToMessageEncoder<MessageLiteOrBuilder> {
public ProtobufEncoder() {
}
protected void encode(ChannelHandlerContext ctx, MessageLiteOrBuilder msg, List<Object> out) throws Exception {
if (msg instanceof MessageLite) {
out.add(Unpooled.wrappedBuffer(((MessageLite)msg).toByteArray()));
} else {
if (msg instanceof Builder) {
out.add(Unpooled.wrappedBuffer(((Builder)msg).build().toByteArray()));
}
}
}
}
So once I registered "new ProtobufEncoder()" in the Netty Channel Pipeline, I can just use "return replyBuilder.build()" - that is correct.
2.In "static byte[] result = new byte[1024];", The length of received message is defined casually, and it doesn't matter - until it really receives a message.
When receiving message, I shall always copy the message bytes to a new byte[] with a correct length firstly - or there will be just a 1024-length bytes[], with the data I need at the beginning, and several zeroes following, which will certainly fail to be decoded.
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 am queuing all messages to rabbitmq queue and processing those on remote server. Below is my producer and reply handler in same class.
public class AmqpAsynchRpcItemWriter<T> implements ItemWriter<T>,
MessageListener {
protected String exchange;
protected String routingKey;
protected String queue;
protected String replyQueue;
protected RabbitTemplate template;
// Reply handler
#Override
public void onMessage(Message message) {
try {
String corrId = new String(message.getMessageProperties()
.getCorrelationId(), "UTF-8");
System.out.println("received " + corrId + " from " + this.replyQueue);
} catch (IOException e) {
e.printStackTrace();
}
}
//Producer
#Override
public void write(List<? extends T> items) throws Exception {
for (T item : items) {
System.out.println(item);
System.out.println("Queing " + item + " to " + this.queue);
Message message = MessageBuilder
.withBody(item.toString().getBytes())
.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN)
.setReplyTo(this.replyQueue)
.setCorrelationId(item.toString().getBytes()).build();
template.send(this.exchange, this.routingKey, message);
System.out.println("Queued " + item + " to " + this.queue);
}
// It should wait here untill we get all replies in onMessage, How can we do this ?
}
I am sending all messages in write method and getting replies in onMessage. This is working properly but write doesnt wait for replies, it returns to caller and spring-batch step is marked completed.
But I want the process to wait for replies after sending all message till we get all replies in onMessage. How can we do this ?
You can use any number of synchronization techniques; for example have the listener put the replies in a LinkedBlockingQueue and have the sender take (or poll with timeout) from the queue until all the replies are received.
Or, don't use a listener at all and simply use the same RabbitTemplate to receive() from the reply queue until all the replies are received.
However, receive() returns null if the queue is empty so you'll have to sleep between receives to avoid spinning the CPU.
Background
I'm working of a proof of concept that consist of a server-client system using websocket communication. I use a small jetty solution (jetty-all-9.1.3.v20140225.jar together with servlet-api-3.1.jar). I have most of the basic functionality in order for a PoC.
I have 2 classes:
TestServer (With a main function creating a Server instance, see code)
ClientSocket (WebSocket-object instantiated for each client)
Problem
The problem I would like to discuss has to do with broadcasting client disconnects. The server saves all instances of ClientSockets in one array, the ClientSockets are added to this array in their "onConnect"-function.
The system will later on limit broadcasts to groups of clients, but for the PoC, all connected clients shall get broadcasts. If one client disconnects I want to send a notification to all other clients ("myClient has disconnected." or similar).
To do this I implement a broadcast function in the server that loops through the list of clients, sending this information to all connected clients except the one who disconnected. This function is also used to inform all clients about other things, such as new connections, and this problem should most likely occur here as well in the case a client disconnects at the very same time as someone connects or broadcasts something.
The problem is easy to produced by connecting several (10+) clients (I do it in js) and then disconnect them all at the same time. If I do this I always get concurrency errors, for very few clients (2-3) it works sometimes, depending on timing I guess.
Questions
How should I handle the task of broadcasting to all other clients, taking into account that any clients can disconnect (asynchronously) at any time? Can I do this without generating exceptions? Since it's asynchronous I can't see any other way than dealing with the exceptions occurring. Any suggestions are greatly appreciated.
Code
TestServer.java
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.websocket.server.WebSocketHandler;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
public class TestServer {
private static final TestServer testServer = new TestServer();
private ArrayList<ClientSocket> clients = new ArrayList<>();
public static void main(String[] args) throws Exception {
int port = 8080;
Server server = new Server(port);
WebSocketHandler wsHandler = new WebSocketHandler() {
#Override
public void configure(WebSocketServletFactory factory) {
factory.register(ClientSocket.class);
}
};
server.setHandler(wsHandler);
System.out.println("Starting server on port " + port + ".");
server.start();
server.join();
}
public static TestServer getServer() {
return testServer;
}
public void addClient(ClientSocket client) {
this.clients.add(client);
}
public void removeClient(ClientSocket client) {
this.clients.remove(client);
this.broadcast("disconnect " + client.id, client);
}
public void broadcast(String message, ClientSocket excludedClient) {
log("Sending to all clients: " + message);
for (ClientSocket cs : this.clients) {
if (!cs.equals(excludedClient) && cs.session.isOpen() && cs != null) {
try {
cs.session.getRemote().sendStringByFuture(message);
} catch (Exception e) {
log("Error when broadcasting to " + cs.id + " (" + cs.address + "):");
log(e.getMessage());
}
}
}
}
Since looping through an array in this way generally doesn't work if you meddle with the array in the process so I also tried this broadcast function:
public void broadcast(String message, ClientSocket excludedClient) {
log("Sending to all clients: " + message);
Iterator<ClientSocket> cs = this.clients.iterator();
while (cs.hasNext()) {
ClientSocket client = cs.next();
if (client != null) {
if (!client.equals(excludedClient) && client.session.isOpen()) {
try {
client.session.getRemote().sendStringByFuture(message);
} catch (Exception e) {
log("Error when broadcasting to " + client.id + " (" + client.address + "):");
log(e.getMessage());
}
}
}
}
It doesn't work any better though, since the problem is that the array can be meddled with asynchronously if another ClientSocket object disconnects as the first one is broadcasting it's disconnection.
ClientSocket.java
import java.io.IOException;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
#WebSocket(maxIdleTime=0)
public class ClientSocket {
TestServer server = TestServer.getServer();
Session session;
String id;
String address;
#OnWebSocketClose
public void onClose(Session session, int statusCode, String reason) {
server.log("Disconnected: " + this.id + "(" + this.address + ")" + " (statusCode=" + statusCode + ", reason=" + reason + ")");
server.removeClient(this);
}
#OnWebSocketError
public void onError(Session session, Throwable t) {
server.log(this.id + "(" + this.address + ") error: " + t.getMessage());
}
#OnWebSocketConnect
public void onConnect(Session session) {
this.session = session;
this.address = this.session.getRemoteAddress().getAddress().toString().substring(1);
this.id = this.address; //Until user is registered their id is their IP
server.log("New connection: " + this.address);
server.addClient(this);
try {
session.getRemote().sendString("Hello client with address " + this.address + "!");
} catch (IOException e) {
server.log("Error in onConnect for " + this.id + "(" + this.address + "): " + e.getMessage());
}
}
#OnWebSocketMessage
public void onMessage(Session session, String message) {
server.log("Received from " + this.id + "(" + this.address + "): " + message);
String replyMessage;
String[] commandList = message.split("\\s+");
switch (commandList[0].toLowerCase()) {
case "register":
if (commandList.length > 1) {
this.id = commandList[1];
replyMessage = "Registered on server as " + this.id;
server.broadcast(this.id + " has connected", this);
} else {
replyMessage = "Incorrect register message";
}
break;
default:
replyMessage = "echo " + message;
break;
}
server.log("Sending to " + this.id + "(" + this.address + "): " + replyMessage);
try {
session.getRemote().sendString(replyMessage);
} catch (IOException e) {
server.log("Error during reply in onMessage for " + this.id + "(" + this.address + "): " + e.getMessage());
}
}
}
I pasted this whole class for completeness, even though I removed some of the cases in the switch for onMessage. The parts to take notice in however are the onConnect and onClose functions that will populate and remove clients from the client array in the server.
Error log
[2014-04-17 17:40:17.961] Sending to all clients: disconnect testclient4
2014-04-17 17:40:17.962:WARN:ClientSocket:qtp29398564-17: Unhandled Error (closing connection)
org.eclipse.jetty.websocket.api.WebSocketException: Cannot call method public void ClientSocket#onClose(org.eclipse.jetty.websocket.api.Session, int, java.lang.String) with args: [org.eclipse.jetty.websocket.common.WebSocketSession, java.lang.Integer, <null>]
at org.eclipse.jetty.websocket.common.events.annotated.CallableMethod.call(CallableMethod.java:99)
at org.eclipse.jetty.websocket.common.events.annotated.OptionalSessionCallableMethod.call(OptionalSessionCallableMethod.java:68)
at org.eclipse.jetty.websocket.common.events.JettyAnnotatedEventDriver.onClose(JettyAnnotatedEventDriver.java:122)
at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.incomingFrame(AbstractEventDriver.java:125)
at org.eclipse.jetty.websocket.common.WebSocketSession.incomingFrame(WebSocketSession.java:302)
at org.eclipse.jetty.websocket.common.extensions.AbstractExtension.nextIncomingFrame(AbstractExtension.java:163)
at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.nextIncomingFrame(PerMessageDeflateExtension.java:92)
at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.incomingFrame(PerMessageDeflateExtension.java:66)
at org.eclipse.jetty.websocket.common.extensions.ExtensionStack.incomingFrame(ExtensionStack.java:210)
at org.eclipse.jetty.websocket.common.Parser.notifyFrame(Parser.java:219)
at org.eclipse.jetty.websocket.common.Parser.parse(Parser.java:257)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.read(AbstractWebSocketConnection.java:500)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:409)
at org.eclipse.jetty.io.AbstractConnection$1.run(AbstractConnection.java:505)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:607)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:536)
at java.lang.Thread.run(Thread.java:744)
Caused by:
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.eclipse.jetty.websocket.common.events.annotated.CallableMethod.call(CallableMethod.java:71)
at org.eclipse.jetty.websocket.common.events.annotated.OptionalSessionCallableMethod.call(OptionalSessionCallableMethod.java:68)
at org.eclipse.jetty.websocket.common.events.JettyAnnotatedEventDriver.onClose(JettyAnnotatedEventDriver.java:122)
at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.incomingFrame(AbstractEventDriver.java:125)
at org.eclipse.jetty.websocket.common.WebSocketSession.incomingFrame(WebSocketSession.java:302)
at org.eclipse.jetty.websocket.common.extensions.AbstractExtension.nextIncomingFrame(AbstractExtension.java:163)
at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.nextIncomingFrame(PerMessageDeflateExtension.java:92)
at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.incomingFrame(PerMessageDeflateExtension.java:66)
at org.eclipse.jetty.websocket.common.extensions.ExtensionStack.incomingFrame(ExtensionStack.java:210)
at org.eclipse.jetty.websocket.common.Parser.notifyFrame(Parser.java:219)
at org.eclipse.jetty.websocket.common.Parser.parse(Parser.java:257)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.read(AbstractWebSocketConnection.java:500)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:409)
at org.eclipse.jetty.io.AbstractConnection$1.run(AbstractConnection.java:505)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:607)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:536)
at java.lang.Thread.run(Thread.java:744)
Caused by:
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at TestServer.broadcast(TestServer.java:61)
at TestServer.removeClient(TestServer.java:45)
at ClientSocket.onClose(ClientSocket.java:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.eclipse.jetty.websocket.common.events.annotated.CallableMethod.call(CallableMethod.java:71)
at org.eclipse.jetty.websocket.common.events.annotated.OptionalSessionCallableMethod.call(OptionalSessionCallableMethod.java:68)
at org.eclipse.jetty.websocket.common.events.JettyAnnotatedEventDriver.onClose(JettyAnnotatedEventDriver.java:122)
at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.incomingFrame(AbstractEventDriver.java:125)
at org.eclipse.jetty.websocket.common.WebSocketSession.incomingFrame(WebSocketSession.java:302)
at org.eclipse.jetty.websocket.common.extensions.AbstractExtension.nextIncomingFrame(AbstractExtension.java:163)
at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.nextIncomingFrame(PerMessageDeflateExtension.java:92)
at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.incomingFrame(PerMessageDeflateExtension.java:66)
at org.eclipse.jetty.websocket.common.extensions.ExtensionStack.incomingFrame(ExtensionStack.java:210)
at org.eclipse.jetty.websocket.common.Parser.notifyFrame(Parser.java:219)
at org.eclipse.jetty.websocket.common.Parser.parse(Parser.java:257)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.read(AbstractWebSocketConnection.java:500)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:409)
at org.eclipse.jetty.io.AbstractConnection$1.run(AbstractConnection.java:505)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:607)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:536)
at java.lang.Thread.run(Thread.java:744)
[2014-04-17 17:40:17.97] testclient7(94.246.80.30) error: Cannot call method public void ClientSocket#onClose(org.eclipse.jetty.websocket.api.Session, int, java.lang.String) with args: [org.eclipse.jetty.websocket.common.WebSocketSession, java.lang.Integer, <null>]
This happens, sometimes for several clients, when disconnecting all the clients at the same time. I'm pretty sure that it has to do with ClientSocket objects disappearing at the same time as a broadcast is made.
Replace:
private ArrayList<ClientSocket> clients = new ArrayList<>();
With:
private List<ClientSocket> clients = new CopyOnWriteArrayList<>();
In your broadcast method just use the for each statement:
for( ClientSocket client : clients )
{
if ( !client.equals(excludedClient) && client.session.isOpen() )
{
// Broadcast...
}
}
This will give you thread safe iteration. ConcurrentModificationException occurs because you are modifying the List at the same time as iterating over it. A CopyOnWriteArrayList copies the internal array on write so modifications do not interfere with iteration. This adds some additional overhead to mutating the list of course, so you may want to think about another method of ensuring thread safety, however I suspect this will be sufficient for your needs. If reads are a lots more common than writes (as is usually the case) then a 'copy on write' data structure will do fine.
it says Stream Closed error
public class StreamAPI {
public static void main(String[] args) {
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(true);
cb.setOAuthConsumerKey("xxxx");
cb.setOAuthConsumerSecret("xxx");
cb.setOAuthAccessToken("xx-xx");
cb.setOAuthAccessTokenSecret("xxx");
cb.setUseSSL(true);
cb.setUserStreamRepliesAllEnabled(true);
TwitterStream twitterStream = new TwitterStreamFactory(cb.build()).getInstance();
AccessToken accestoken = new AccessToken("xx-xx", "xxx");
twitterStream.setOAuthAccessToken(accestoken);
StatusListener listener = new StatusListener() {
public void onStatus(Status status) {
System.out.println("#" + status.getUser().getScreenName() + " - " + status.getText());
}
public void onDeletionNotice(StatusDeletionNotice statusDeletionNotice) {
System.out.println("Got a status deletion notice id:" + statusDeletionNotice.getStatusId());
}
public void onTrackLimitationNotice(int numberOfLimitedStatuses) {
System.out.println("Got track limitation notice:" + numberOfLimitedStatuses);
}
public void onScrubGeo(long userId, long upToStatusId) {
System.out.println("Got scrub_geo event userId:" + userId + " upToStatusId:" + upToStatusId);
}
#Override
public void onStallWarning(StallWarning stallWarning) {
//To change body of implemented methods use File | Settings | File Templates.
}
public void onException(Exception ex) {
ex.printStackTrace();
}
};
FilterQuery fq = new FilterQuery();
String keywords[] = {"France", "Germany"};
fq.track(keywords);
twitterStream.addListener(listener);
twitterStream.filter(fq);
}
}
i am getting error
Stream closed.
Relevant discussions can be found on the Internet at:
http://www.google.co.jp/search?q=a8fd061d or
http://www.google.co.jp/search?q=00070a0c
TwitterException{exceptionCode=[a8fd061d-00070a0c a8fd061d-0007099d], statusCode=-1, message=null, code=-1, retryAfter=-1, rateLimitStatus=null, version=3.0.3}
at twitter4j.StatusStreamBase.handleNextElement(StatusStreamBase.java:199)
at twitter4j.StatusStreamImpl.next(StatusStreamImpl.java:57)
at twitter4j.TwitterStreamImpl$TwitterStreamConsumer.run(TwitterStreamImpl.java:478)
Caused by: java.io.IOException: the end of the stream has been reached
at twitter4j.StatusStreamBase.handleNextElement(StatusStreamBase.java:88)
... 2 more
But the same configuration works for TwitterAPI
Twitter gives the following reasons why you may be disconnected:
A client establishes too many connections with the same credentials. When this occurs, the oldest connection will be terminated. This means
you have to be careful not to run two reconnecting clients in parallel
with the same credentials, or else they will take turns disconnecting
each other.
A client stops reading data suddenly. If the rate of Tweets being read off of the stream drops suddenly, the connection will be closed.
A client reads data too slowly. Every streaming connection is backed
by a queue of messages to be sent to the client. If this queue grows
too large over time, the connection will be closed.
A streaming server is restarted. This is usually related to a code deploy and is not very frequent.
Twitter's network configuration changes. These events are extremely rare, and would represent load balancer restarts or network
reconfigurations, for example.
You should receive information on why you were disconnected, but not always:
Streams may be shut down for a variety of reasons. The streaming API will attempt to deliver a message indicating why a stream was closed. Note that if the disconnect was due to network issues or a client reading too slowly, it is possible that this message will not be received.
My thought would be that perhaps you're not reading data quickly enough - although I can't say for certain. Try implementing onStallWarning to see if you're getting any stall warnings, e.g.:
#Override
public void onStallWarning(StallWarning stallWarning) {
System.out.println(stallWarning);
}