I'm currently doing quick tests and demos with SFS2x Java API.
I'm using Swing UI for it.
I can post all the code here, but I'm using a quick example to show the problem...
I have the classe conn.Login.
It's implements the IEventListener SFS2x class for handling events from the client API.
Something like this:
Code: Select all
package conn;
public class Login implements IEventListener {
private SmartFox clientAPI;
public Login(SmartFox client) {
clientAPI = client;
}
@Override
public void dispatch(final BaseEvent event) throws SFSException {
String eventType = event.getType();
if(eventType.equals(SFSEvent.LOGIN)) {
User user = (User)event.getArguments().get("user");
ISFSObject data = (ISFSObject)event.getArguments().get("data");
// Here the login success will be notified to UI interface, so the SwingUI can show that Login is done.
// The method that will be called is onLoginSuccess().
}
else if(eventType.equals(SFSEvent.LOGIN_ERROR)) {
String errorMessage = (String)event.getArguments().get("errorMessage");
short errorCode = (Short)event.getArguments().get("errorCode");
// Here the login error will be notified to UI interface, so the SwingUI can show that Login had errors..
}
else {}
}
public final void init() {
clientAPI.addEventListener(SFSEvent.LOGIN, this);
clientAPI.addEventListener(SFSEvent.LOGIN_ERROR, this);
}
public final void free() {
clientAPI.removeEventListener(SFSEvent.LOGIN, this);
clientAPI.removeEventListener(SFSEvent.LOGIN_ERROR, this);
}
}
The first problem is:
When I receive the login, I want to free the class conn.Login.
The best time to do it is when the login is successful. So in the Controller's of SwingUI class I put something like it:
Code: Select all
// Method called from the conn.Login::dispatch(SFSEvent.LOGIN).
public final void onLoginSuccess() {
myAppController.freeLogin();
}
myAppController is the class that bind SwingUI (create view) and also has the conn.Login instance.
The freeLogin() methods is going to call conn.Login::free() that will removeEventListeners from clientAPI.
That happens in the same call of dispatch() that is using the event listeners list in the SFS2x Client API.
Then occurs the Concurrency Problem:
Code: Select all
java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at sfs2x.client.core.EventDispatcher.dispatchEvent(EventDispatcher.java:51)
at sfs2x.client.SmartFox.dispatchEvent(SmartFox.java:1265)
at sfs2x.client.controllers.system.ResLogout.handleResponse(ResLogout.java:26)
at sfs2x.client.controllers.SystemController.handleMessage(SystemController.java:104)
at sfs2x.client.core.SFSProtocolCodec.dispatchRequest(SFSProtocolCodec.java:144)
at sfs2x.client.core.SFSProtocolCodec.onPacketRead(SFSProtocolCodec.java:50)
at sfs2x.client.core.SFSIOHandler.handlePacketData(SFSIOHandler.java:281)
at sfs2x.client.core.SFSIOHandler.onDataRead(SFSIOHandler.java:130)
at sfs2x.client.bitswarm.BitSwarmClient.onSocketData(BitSwarmClient.java:354)
at sfs2x.client.bitswarm.BitSwarmClient.access$2(BitSwarmClient.java:348)
at sfs2x.client.bitswarm.BitSwarmClient$3.dispatch(BitSwarmClient.java:105)
at sfs2x.client.core.EventDispatcher.dispatchEvent(EventDispatcher.java:52)
at sfs2x.client.core.sockets.TCPSocketLayer.callOnData(TCPSocketLayer.java:144)
at sfs2x.client.core.sockets.TCPSocketLayer.handleBinaryData(TCPSocketLayer.java:138)
at sfs2x.client.core.sockets.TCPSocketLayer.access$6(TCPSocketLayer.java:137)
at sfs2x.client.core.sockets.TCPSocketLayer$NettyIOHandler.messageReceived(TCPSocketLayer.java:273)
at org.jboss.netty.channel.SimpleChannelHandler.handleUpstream(SimpleChannelHandler.java:100)
at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:545)
at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:540)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:274)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:261)
at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:350)
at org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:281)
at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:201)
at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108)
at org.jboss.netty.util.internal.IoWorkerRunnable.run(IoWorkerRunnable.java:46)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:680)
How I can remove the eventListeners of Login (aka free the conn.Login class) after the event login success?
The same problem will happen with ROOM_JOIN::
There are a "Global Lobby Room" that I enter by default
Inside it, I can stay there or go to 5 rooms I can choose (North America, South America, Europe, Australia, Asia)
The first ROOM_JOIN event. I need to remove it from the conn.Lobby class after I enter this room "Global Lobby Room"., so I can enable and use the ROOM_JOIN event in the class conn.CountryRoom to enter the "South America" room.
But will fall in Concurrency problem, like the LOGIN. Because I'm calling the removeEventListener(SFSEvent.ROOM_JOIN...) when the dispatch happens.
Any clues?
I will need one more Thread and queue to remove the handlers?
There's another way?
Because I can receive a private message, per example, at same moment that the other Thread is removing the eventListener.
Thanks in advance!
R.