Browse Source

Event typization support added. Issue #23

master
Nikita 13 years ago
parent
commit
b7b61c0717
  1. 8
      src/main/java/com/corundumstudio/socketio/SocketIOServer.java
  2. 2
      src/main/java/com/corundumstudio/socketio/listener/ClientListeners.java
  3. 6
      src/main/java/com/corundumstudio/socketio/listener/DataListener.java
  4. 45
      src/main/java/com/corundumstudio/socketio/namespace/EventEntry.java
  5. 27
      src/main/java/com/corundumstudio/socketio/namespace/Namespace.java
  6. 9
      src/main/java/com/corundumstudio/socketio/namespace/NamespacesHub.java
  7. 62
      src/main/java/com/corundumstudio/socketio/parser/JacksonJsonSupport.java
  8. 4
      src/main/java/com/corundumstudio/socketio/parser/JsonSupport.java
  9. 2
      src/test/java/com/corundumstudio/socketio/PacketHandlerTest.java
  10. 5
      src/test/java/com/corundumstudio/socketio/parser/DecoderEventPacketTest.java

8
src/main/java/com/corundumstudio/socketio/SocketIOServer.java

@ -37,7 +37,7 @@ public class SocketIOServer implements ClientListeners {
private ServerBootstrap bootstrap;
private final NamespacesHub namespacesHub = new NamespacesHub();
private final NamespacesHub namespacesHub;
private final SocketIONamespace mainNamespace;
private SocketIOPipelineFactory pipelineFactory = new SocketIOPipelineFactory();
@ -48,7 +48,7 @@ public class SocketIOServer implements ClientListeners {
public SocketIOServer(Configuration configuration) {
this.config = new Configuration(configuration);
namespacesHub = new NamespacesHub(this.config.getJsonSupport());
mainNamespace = addNamespace(Namespace.DEFAULT_NAME);
}
@ -103,8 +103,8 @@ public class SocketIOServer implements ClientListeners {
}
@Override
public void addEventListener(String eventName, DataListener<Object> listener) {
mainNamespace.addEventListener(eventName, listener);
public <T> void addEventListener(String eventName, Class<T> eventClass, DataListener<T> listener) {
mainNamespace.addEventListener(eventName, eventClass, listener);
}
@Override

2
src/main/java/com/corundumstudio/socketio/listener/ClientListeners.java

@ -17,7 +17,7 @@ package com.corundumstudio.socketio.listener;
public interface ClientListeners {
void addEventListener(String eventName, DataListener<Object> listener);
<T> void addEventListener(String eventName, Class<T> eventClass, DataListener<T> listener);
void addJsonObjectListener(DataListener<Object> listener);

6
src/main/java/com/corundumstudio/socketio/listener/DataListener.java

@ -19,6 +19,12 @@ import com.corundumstudio.socketio.SocketIOClient;
public interface DataListener<T> {
/**
* Invokes when data object received from client
*
* @param client - receiver
* @param data - received object
*/
void onData(SocketIOClient client, T data);
}

45
src/main/java/com/corundumstudio/socketio/namespace/EventEntry.java

@ -0,0 +1,45 @@
/**
* Copyright 2012 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.corundumstudio.socketio.namespace;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import com.corundumstudio.socketio.listener.DataListener;
public class EventEntry<T> {
private final Class<T> eventClass;
private final Queue<DataListener<T>> listeners = new ConcurrentLinkedQueue<DataListener<T>>();;
public EventEntry(Class<T> eventClass) {
super();
this.eventClass = eventClass;
}
public Class<T> getEventClass() {
return eventClass;
}
public void addListener(DataListener<T> listener) {
listeners.add(listener);
}
public Queue<DataListener<T>> getListeners() {
return listeners;
}
}

27
src/main/java/com/corundumstudio/socketio/namespace/Namespace.java

@ -29,6 +29,7 @@ import com.corundumstudio.socketio.SocketIONamespace;
import com.corundumstudio.socketio.listener.ConnectListener;
import com.corundumstudio.socketio.listener.DataListener;
import com.corundumstudio.socketio.listener.DisconnectListener;
import com.corundumstudio.socketio.parser.JsonSupport;
import com.corundumstudio.socketio.transport.NamespaceClient;
public class Namespace implements SocketIONamespace {
@ -36,18 +37,20 @@ public class Namespace implements SocketIONamespace {
public static final String DEFAULT_NAME = "";
private final Set<SocketIOClient> clients = Collections.newSetFromMap(new ConcurrentHashMap<SocketIOClient, Boolean>());
private final ConcurrentMap<String, Queue<DataListener<Object>>> eventListeners =
new ConcurrentHashMap<String, Queue<DataListener<Object>>>();
private final ConcurrentMap<String, EventEntry<?>> eventListeners =
new ConcurrentHashMap<String, EventEntry<?>>();
private final Queue<DataListener<Object>> jsonObjectListeners = new ConcurrentLinkedQueue<DataListener<Object>>();
private final Queue<DataListener<String>> messageListeners = new ConcurrentLinkedQueue<DataListener<String>>();
private final Queue<ConnectListener> connectListeners = new ConcurrentLinkedQueue<ConnectListener>();
private final Queue<DisconnectListener> disconnectListeners = new ConcurrentLinkedQueue<DisconnectListener>();
private final String name;
private final JsonSupport jsonSupport;
public Namespace(String name) {
public Namespace(String name, JsonSupport jsonSupport) {
super();
this.name = name;
this.jsonSupport = jsonSupport;
}
public void addClient(SocketIOClient client) {
@ -59,21 +62,25 @@ public class Namespace implements SocketIONamespace {
}
@Override
public void addEventListener(String eventName, DataListener<Object> listener) {
Queue<DataListener<Object>> entry = eventListeners.get(eventName);
@SuppressWarnings({"unchecked", "rawtypes"})
public <T> void addEventListener(String eventName, Class<T> eventClass, DataListener<T> listener) {
EventEntry entry = eventListeners.get(eventName);
if (entry == null) {
entry = new ConcurrentLinkedQueue<DataListener<Object>>();
Queue<DataListener<Object>> oldEntry = eventListeners.putIfAbsent(eventName, entry);
entry = new EventEntry<T>(eventClass);
EventEntry<?> oldEntry = eventListeners.putIfAbsent(eventName, entry);
if (oldEntry != null) {
entry = oldEntry;
}
}
entry.add(listener);
entry.addListener(listener);
jsonSupport.addEventMapping(eventName, eventClass);
}
@SuppressWarnings({"rawtypes", "unchecked"})
public void onEvent(SocketIOClient client, String eventName, Object data) {
Queue<DataListener<Object>> entry = eventListeners.get(eventName);
for (DataListener<Object> dataListener : entry) {
EventEntry entry = eventListeners.get(eventName);
Queue<DataListener> listeners = entry.getListeners();
for (DataListener dataListener : listeners) {
dataListener.onData(client, data);
}
}

9
src/main/java/com/corundumstudio/socketio/namespace/NamespacesHub.java

@ -18,14 +18,21 @@ package com.corundumstudio.socketio.namespace;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.corundumstudio.socketio.parser.JsonSupport;
public class NamespacesHub {
private final ConcurrentMap<String, Namespace> namespaces = new ConcurrentHashMap<String, Namespace>();
private final JsonSupport jsonSupport;
public NamespacesHub(JsonSupport jsonSupport) {
this.jsonSupport = jsonSupport;
}
public Namespace create(String name) {
Namespace namespace = namespaces.get(name);
if (namespace == null) {
namespace = new Namespace(name);
namespace = new Namespace(name, jsonSupport);
Namespace oldNamespace = namespaces.putIfAbsent(name, namespace);
if (oldNamespace != null) {
namespace = oldNamespace;

62
src/main/java/com/corundumstudio/socketio/parser/JacksonJsonSupport.java

@ -18,16 +18,78 @@ package com.corundumstudio.socketio.parser;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.map.deser.std.StdDeserializer;
import org.codehaus.jackson.map.module.SimpleModule;
import org.codehaus.jackson.node.ObjectNode;
public class JacksonJsonSupport implements JsonSupport {
private class EventDeserializer extends StdDeserializer<Event> {
Map<String, Class<?>> eventMapping = new ConcurrentHashMap<String, Class<?>>();
protected EventDeserializer() {
super(Event.class);
}
@Override
public Event deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
JsonProcessingException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
ObjectNode root = (ObjectNode) mapper.readTree(jp);
String eventName = root.get("name").asText();
List eventArgs = new ArrayList();
Event event = new Event(eventName, eventArgs);
JsonNode args = root.get("args");
if (args != null) {
Iterator<JsonNode> iterator = args.getElements();
if (iterator.hasNext()) {
JsonNode node = iterator.next();
Class<?> eventClass = eventMapping.get(eventName);
Object arg = mapper.readValue(node, eventClass);
eventArgs.add(arg);
while (iterator.hasNext()) {
node = iterator.next();
arg = mapper.readValue(node, Object.class);
eventArgs.add(arg);
}
}
}
return event;
}
}
private final ObjectMapper objectMapper = new ObjectMapper();
private final EventDeserializer eventDeserializer = new EventDeserializer();
public JacksonJsonSupport() {
SimpleModule module = new SimpleModule("EventDeserializerModule", new Version(1, 0, 0, null));
module.addDeserializer(Event.class, eventDeserializer);
objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
objectMapper.registerModule(module);
}
public void addEventMapping(String eventName, Class<?> eventClass) {
eventDeserializer.eventMapping.put(eventName, eventClass);
}
public void removeEventMapping(String eventName) {
eventDeserializer.eventMapping.remove(eventName);
}
@Override

4
src/main/java/com/corundumstudio/socketio/parser/JsonSupport.java

@ -27,4 +27,8 @@ public interface JsonSupport {
String writeValueAsString(Object value) throws IOException;
void addEventMapping(String eventName, Class<?> eventClass);
void removeEventMapping(String eventName);
}

2
src/test/java/com/corundumstudio/socketio/PacketHandlerTest.java

@ -48,7 +48,7 @@ public class PacketHandlerTest {
private JsonSupport map = new JacksonJsonSupport();
private Decoder decoder = new Decoder(map);
private Encoder encoder = new Encoder(map);
private NamespacesHub namespacesHub = new NamespacesHub();
private NamespacesHub namespacesHub = new NamespacesHub(map);
@Mocked
private Channel channel;
@Mocked

5
src/test/java/com/corundumstudio/socketio/parser/DecoderEventPacketTest.java

@ -16,6 +16,7 @@
package com.corundumstudio.socketio.parser;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
@ -41,6 +42,10 @@ public class DecoderEventPacketTest extends DecoderBaseTest {
@Test
public void testDecodeWithData() throws IOException {
JacksonJsonSupport jsonSupport = new JacksonJsonSupport();
Decoder decoder = new Decoder(jsonSupport);
jsonSupport.addEventMapping("edwald", HashMap.class);
Packet packet = decoder.decodePacket("5:::{\"name\":\"edwald\",\"args\":[{\"a\": \"b\"},2,\"3\"]}");
Assert.assertEquals(PacketType.EVENT, packet.getType());
Assert.assertEquals("edwald", packet.getName());

Loading…
Cancel
Save