Browse Source

Support for Multiple Arguments to EventListeners. Issue #100

master
Nikita 12 years ago
parent
commit
a20ce21110
  1. 5
      src/main/java/com/corundumstudio/socketio/BroadcastOperations.java
  2. 2
      src/main/java/com/corundumstudio/socketio/ClientOperations.java
  3. 2
      src/main/java/com/corundumstudio/socketio/JsonSupportWrapper.java
  4. 26
      src/main/java/com/corundumstudio/socketio/MultiTypeArgs.java
  5. 2
      src/main/java/com/corundumstudio/socketio/SocketIOClient.java
  6. 6
      src/main/java/com/corundumstudio/socketio/SocketIOServer.java
  7. 5
      src/main/java/com/corundumstudio/socketio/annotation/OnEvent.java
  8. 64
      src/main/java/com/corundumstudio/socketio/annotation/OnEventScanner.java
  9. 12
      src/main/java/com/corundumstudio/socketio/handler/PacketListener.java
  10. 2
      src/main/java/com/corundumstudio/socketio/listener/ClientListeners.java
  11. 26
      src/main/java/com/corundumstudio/socketio/listener/MultiTypeEventListener.java
  12. 8
      src/main/java/com/corundumstudio/socketio/namespace/EventEntry.java
  13. 30
      src/main/java/com/corundumstudio/socketio/namespace/Namespace.java
  14. 71
      src/main/java/com/corundumstudio/socketio/parser/JacksonJsonSupport.java
  15. 2
      src/main/java/com/corundumstudio/socketio/parser/JsonSupport.java
  16. 10
      src/main/java/com/corundumstudio/socketio/transport/NamespaceClient.java

5
src/main/java/com/corundumstudio/socketio/BroadcastOperations.java

@ -16,6 +16,7 @@
package com.corundumstudio.socketio;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@ -121,10 +122,10 @@ public class BroadcastOperations implements ClientOperations {
}
@Override
public void sendEvent(String name, Object data) {
public void sendEvent(String name, Object... data) {
Packet packet = new Packet(PacketType.EVENT);
packet.setName(name);
packet.setArgs(Collections.singletonList(data));
packet.setArgs(Arrays.asList(data));
send(packet);
}

2
src/main/java/com/corundumstudio/socketio/ClientOperations.java

@ -56,6 +56,6 @@ public interface ClientOperations {
* @param name - event name
* @param data - event data
*/
void sendEvent(String name, Object data);
void sendEvent(String name, Object ... data);
}

2
src/main/java/com/corundumstudio/socketio/JsonSupportWrapper.java

@ -86,7 +86,7 @@ class JsonSupportWrapper implements JsonSupport {
}
}
public void addEventMapping(String eventName, Class<?> eventClass) {
public void addEventMapping(String eventName, Class<?> ... eventClass) {
delegate.addEventMapping(eventName, eventClass);
}

26
src/main/java/com/corundumstudio/socketio/MultiTypeArgs.java

@ -15,9 +15,10 @@
*/
package com.corundumstudio.socketio;
import java.util.Iterator;
import java.util.List;
public class MultiTypeArgs {
public class MultiTypeArgs implements Iterable<Object> {
private final List<Object> args;
@ -26,12 +27,33 @@ public class MultiTypeArgs {
this.args = args;
}
public boolean isEmpty() {
return size() == 0;
}
public int size() {
return args.size();
}
public List<Object> getArgs() {
return args;
}
public <T> T first() {
return get(0);
}
public <T> T second() {
return get(1);
}
public <T> T get(int index) {
return (T) args.get(0);
return (T) args.get(index);
}
@Override
public Iterator<Object> iterator() {
return args.iterator();
}
}

2
src/main/java/com/corundumstudio/socketio/SocketIOClient.java

@ -50,7 +50,7 @@ public interface SocketIOClient extends ClientOperations, Store {
* @param data - event data
* @param ackCallback - ack callback
*/
void sendEvent(String name, Object data, AckCallback<?> ackCallback);
void sendEvent(String name, AckCallback<?> ackCallback, Object ... data);
/**
* Send packet with ack callback

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

@ -31,6 +31,7 @@ import com.corundumstudio.socketio.listener.ClientListeners;
import com.corundumstudio.socketio.listener.ConnectListener;
import com.corundumstudio.socketio.listener.DataListener;
import com.corundumstudio.socketio.listener.DisconnectListener;
import com.corundumstudio.socketio.listener.MultiTypeEventListener;
import com.corundumstudio.socketio.namespace.Namespace;
import com.corundumstudio.socketio.namespace.NamespacesHub;
@ -151,6 +152,11 @@ public class SocketIOServer implements ClientListeners {
return configuration;
}
@Override
public void addMultiTypeEventListener(String eventName, MultiTypeEventListener listener, Class<?>... eventClass) {
mainNamespace.addMultiTypeEventListener(eventName, listener, eventClass);
}
@Override
public <T> void addEventListener(String eventName, Class<T> eventClass, DataListener<T> listener) {
mainNamespace.addEventListener(eventName, eventClass, listener);

5
src/main/java/com/corundumstudio/socketio/annotation/OnEvent.java

@ -40,4 +40,9 @@ public @interface OnEvent {
*/
String value();
/**
* An array of the classes for multi type event.
*/
Class<?>[] classes() default {};
}

64
src/main/java/com/corundumstudio/socketio/annotation/OnEventScanner.java

@ -19,9 +19,11 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.MultiTypeArgs;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.handler.SocketIOException;
import com.corundumstudio.socketio.listener.DataListener;
import com.corundumstudio.socketio.listener.MultiTypeEventListener;
import com.corundumstudio.socketio.namespace.Namespace;
public class OnEventScanner implements AnnotationScanner {
@ -41,30 +43,58 @@ public class OnEventScanner implements AnnotationScanner {
final int socketIOClientIndex = paramIndex(method, SocketIOClient.class);
final int ackRequestIndex = paramIndex(method, AckRequest.class);
final int dataIndex = dataIndex(method);
Class objectType = Void.class;
if (dataIndex != -1) {
objectType = method.getParameterTypes()[dataIndex];
}
namespace.addEventListener(annotation.value(), objectType, new DataListener<Object>() {
@Override
public void onData(SocketIOClient client, Object data, AckRequest ackSender) {
try {
Object[] args = new Object[method.getParameterTypes().length];
if (socketIOClientIndex != -1) {
args[socketIOClientIndex] = client;
}
if (ackRequestIndex != -1) {
args[ackRequestIndex] = ackSender;
if (MultiTypeArgs.class.isAssignableFrom(objectType)) {
if (annotation.classes().length == 0) {
throw new IllegalArgumentException("OnEvent \"classes\" parameter is required when MultiTypeArgs declared as method argument");
}
namespace.addMultiTypeEventListener(annotation.value(), new MultiTypeEventListener() {
@Override
public void onData(SocketIOClient client, MultiTypeArgs data, AckRequest ackSender) {
try {
Object[] args = new Object[method.getParameterTypes().length];
if (socketIOClientIndex != -1) {
args[socketIOClientIndex] = client;
}
if (ackRequestIndex != -1) {
args[ackRequestIndex] = ackSender;
}
if (dataIndex != -1) {
args[dataIndex] = data;
}
method.invoke(object, args);
} catch (Exception e) {
throw new SocketIOException(e);
}
if (dataIndex != -1) {
args[dataIndex] = data;
}
}, annotation.classes());
} else {
namespace.addEventListener(annotation.value(), objectType, new DataListener<Object>() {
@Override
public void onData(SocketIOClient client, Object data, AckRequest ackSender) {
try {
Object[] args = new Object[method.getParameterTypes().length];
if (socketIOClientIndex != -1) {
args[socketIOClientIndex] = client;
}
if (ackRequestIndex != -1) {
args[ackRequestIndex] = ackSender;
}
if (dataIndex != -1) {
args[dataIndex] = data;
}
method.invoke(object, args);
} catch (Exception e) {
throw new SocketIOException(e);
}
method.invoke(object, args);
} catch (Exception e) {
throw new SocketIOException(e);
}
}
});
});
}
}
private int dataIndex(Method method) {

12
src/main/java/com/corundumstudio/socketio/handler/PacketListener.java

@ -16,6 +16,7 @@
package com.corundumstudio.socketio.handler;
import java.util.Collections;
import java.util.List;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.ack.AckManager;
@ -57,13 +58,12 @@ public class PacketListener {
break;
case EVENT: {
Object data = null;
if (packet.getArgs() != null
&& !packet.getArgs().isEmpty()) {
data = packet.getArgs().get(0);
}
Namespace namespace = namespacesHub.get(packet.getEndpoint());
namespace.onEvent(client, packet.getName(), data, ackRequest);
List<Object> args = Collections.emptyList();
if (packet.getArgs() != null) {
args = packet.getArgs();
}
namespace.onEvent(client, packet.getName(), args, ackRequest);
break;
}

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

@ -17,6 +17,8 @@ package com.corundumstudio.socketio.listener;
public interface ClientListeners {
void addMultiTypeEventListener(String eventName, MultiTypeEventListener listener, Class<?> ... eventClass);
<T> void addEventListener(String eventName, Class<T> eventClass, DataListener<T> listener);
<T> void addJsonObjectListener(Class<T> clazz, DataListener<T> listener);

26
src/main/java/com/corundumstudio/socketio/listener/MultiTypeEventListener.java

@ -0,0 +1,26 @@
/**
* 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.listener;
import com.corundumstudio.socketio.MultiTypeArgs;
/**
* Multi type args event listener
*
*/
public interface MultiTypeEventListener extends DataListener<MultiTypeArgs> {
}

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

@ -22,16 +22,10 @@ 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) {
public EventEntry() {
super();
this.eventClass = eventClass;
}
public Class<T> getEventClass() {
return eventClass;
}
public void addListener(DataListener<T> listener) {

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

@ -31,12 +31,14 @@ import org.slf4j.LoggerFactory;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.BroadcastOperations;
import com.corundumstudio.socketio.MultiTypeArgs;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIONamespace;
import com.corundumstudio.socketio.annotation.ScannerEngine;
import com.corundumstudio.socketio.listener.ConnectListener;
import com.corundumstudio.socketio.listener.DataListener;
import com.corundumstudio.socketio.listener.DisconnectListener;
import com.corundumstudio.socketio.listener.MultiTypeEventListener;
import com.corundumstudio.socketio.parser.JsonSupport;
import com.corundumstudio.socketio.parser.Packet;
import com.corundumstudio.socketio.store.StoreFactory;
@ -87,12 +89,27 @@ public class Namespace implements SocketIONamespace {
return name;
}
@Override
public void addMultiTypeEventListener(String eventName, MultiTypeEventListener listener,
Class<?>... eventClass) {
EventEntry entry = eventListeners.get(eventName);
if (entry == null) {
entry = new EventEntry();
EventEntry<?> oldEntry = eventListeners.putIfAbsent(eventName, entry);
if (oldEntry != null) {
entry = oldEntry;
}
}
entry.addListener(listener);
jsonSupport.addEventMapping(eventName, eventClass);
}
@Override
@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 EventEntry<T>(eventClass);
entry = new EventEntry<T>();
EventEntry<?> oldEntry = eventListeners.putIfAbsent(eventName, entry);
if (oldEntry != null) {
entry = oldEntry;
@ -117,13 +134,22 @@ public class Namespace implements SocketIONamespace {
}
@SuppressWarnings({"rawtypes", "unchecked"})
public void onEvent(NamespaceClient client, String eventName, Object data, AckRequest ackRequest) {
public void onEvent(NamespaceClient client, String eventName, List<Object> args, AckRequest ackRequest) {
EventEntry entry = eventListeners.get(eventName);
if (entry == null) {
return;
}
Queue<DataListener> listeners = entry.getListeners();
for (DataListener dataListener : listeners) {
Object data = null;
if (dataListener instanceof MultiTypeEventListener) {
data = new MultiTypeArgs(args);
} else {
if (!args.isEmpty()) {
data = args.get(0);
}
}
dataListener.onData(client, data, ackRequest);
}
}

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

@ -19,8 +19,8 @@ import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@ -28,6 +28,9 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.corundumstudio.socketio.AckCallback;
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.MultiTypeAckCallback;
@ -51,6 +54,8 @@ public class JacksonJsonSupport implements JsonSupport {
private class JsonObjectDeserializer extends StdDeserializer<JsonObject> {
private static final long serialVersionUID = 8025939196914136933L;
final Set<Class<?>> classes = new ConcurrentHashSet<Class<?>>();
protected JsonObjectDeserializer() {
@ -97,6 +102,8 @@ public class JacksonJsonSupport implements JsonSupport {
private class AckArgsDeserializer extends StdDeserializer<AckArgs> {
private static final long serialVersionUID = 7810461017389946707L;
protected AckArgsDeserializer() {
super(AckArgs.class);
}
@ -128,23 +135,23 @@ public class JacksonJsonSupport implements JsonSupport {
}
// TODO refactor it!
if (arg.isNumber()) {
if (clazz.equals(Long.class)) {
val = arg.longValue();
}
if (clazz.equals(BigDecimal.class)) {
val = arg.bigIntegerValue();
}
if (clazz.equals(Double.class)) {
val = arg.doubleValue();
}
if (clazz.equals(Integer.class)) {
val = arg.intValue();
}
if (clazz.equals(Float.class)) {
val = (float)arg.doubleValue();
}
}
// if (arg.isNumber()) {
// if (clazz.equals(Long.class)) {
// val = arg.longValue();
// }
// if (clazz.equals(BigDecimal.class)) {
// val = arg.bigIntegerValue();
// }
// if (clazz.equals(Double.class)) {
// val = arg.doubleValue();
// }
// if (clazz.equals(Integer.class)) {
// val = arg.intValue();
// }
// if (clazz.equals(Float.class)) {
// val = (float)arg.doubleValue();
// }
// }
val = mapper.treeToValue(arg, clazz);
args.add(val);
i++;
@ -156,7 +163,9 @@ public class JacksonJsonSupport implements JsonSupport {
private class EventDeserializer extends StdDeserializer<Event> {
final Map<String, Class<?>> eventMapping = new ConcurrentHashMap<String, Class<?>>();
private static final long serialVersionUID = 8178797221017768689L;
final Map<String, List<Class<?>>> eventMapping = new ConcurrentHashMap<String, List<Class<?>>>();
protected EventDeserializer() {
super(Event.class);
@ -172,21 +181,23 @@ public class JacksonJsonSupport implements JsonSupport {
return new Event(eventName, Collections.emptyList());
}
List eventArgs = new ArrayList();
List<Object> eventArgs = new ArrayList<Object>();
Event event = new Event(eventName, eventArgs);
JsonNode args = root.get("args");
if (args != null) {
Iterator<JsonNode> iterator = args.elements();
if (iterator.hasNext()) {
List<Class<?>> eventClasses = eventMapping.get(eventName);
int i = 0;
while (iterator.hasNext()) {
JsonNode node = iterator.next();
Class<?> eventClass = eventMapping.get(eventName);
if (i > eventClasses.size() - 1) {
log.debug("Event {} has more args than declared in handler: {}", eventName, args);
break;
}
Class<?> eventClass = eventClasses.get(i);
Object arg = mapper.treeToValue(node, eventClass);
eventArgs.add(arg);
while (iterator.hasNext()) {
node = iterator.next();
arg = mapper.treeToValue(node, Object.class);
eventArgs.add(arg);
}
i++;
}
}
return event;
@ -201,6 +212,8 @@ public class JacksonJsonSupport implements JsonSupport {
private final JsonObjectDeserializer jsonObjectDeserializer = new JsonObjectDeserializer();
private final AckArgsDeserializer ackArgsDeserializer = new AckArgsDeserializer();
private final Logger log = LoggerFactory.getLogger(getClass());
public JacksonJsonSupport(Configuration configuration) {
this(configuration, null);
}
@ -234,8 +247,8 @@ public class JacksonJsonSupport implements JsonSupport {
}
@Override
public void addEventMapping(String eventName, Class<?> eventClass) {
eventDeserializer.eventMapping.put(eventName, eventClass);
public void addEventMapping(String eventName, Class<?> ... eventClass) {
eventDeserializer.eventMapping.put(eventName, Arrays.asList(eventClass));
}
@Override

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

@ -40,7 +40,7 @@ public interface JsonSupport {
<T> T readValue(String src, Class<T> valueType) throws IOException;
void addEventMapping(String eventName, Class<?> eventClass);
void addEventMapping(String eventName, Class<?> ... eventClass);
void addJsonClass(Class<?> clazz);

10
src/main/java/com/corundumstudio/socketio/transport/NamespaceClient.java

@ -16,7 +16,7 @@
package com.corundumstudio.socketio.transport;
import java.net.SocketAddress;
import java.util.Collections;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
@ -59,18 +59,18 @@ public class NamespaceClient implements SocketIOClient {
}
@Override
public void sendEvent(String name, Object data) {
public void sendEvent(String name, Object ... data) {
Packet packet = new Packet(PacketType.EVENT);
packet.setName(name);
packet.setArgs(Collections.singletonList(data));
packet.setArgs(Arrays.asList(data));
send(packet);
}
@Override
public void sendEvent(String name, Object data, AckCallback<?> ackCallback) {
public void sendEvent(String name, AckCallback<?> ackCallback, Object ... data) {
Packet packet = new Packet(PacketType.EVENT);
packet.setName(name);
packet.setArgs(Collections.singletonList(data));
packet.setArgs(Arrays.asList(data));
send(packet, ackCallback);
}

Loading…
Cancel
Save