Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve handling of method discovery in AnnotatedEventManager #2454

2 changes: 1 addition & 1 deletion src/main/java/net/dv8tion/jda/api/JDABuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -1817,7 +1817,7 @@ public JDA build()
if (audioSendFactory != null)
jda.setAudioSendFactory(audioSendFactory);

listeners.forEach(jda::addEventListener);
jda.addEventListener(listeners.toArray());
jda.setStatus(JDA.Status.INITIALIZED); //This is already set by JDA internally, but this is to make sure the listeners catch it.

// Set the presence information before connecting to have the correct information ready when sending IDENTIFY
Expand Down
60 changes: 36 additions & 24 deletions src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import net.dv8tion.jda.api.events.GenericEvent;
import net.dv8tion.jda.internal.JDAImpl;
import net.dv8tion.jda.internal.utils.ClassWalker;
import net.dv8tion.jda.internal.utils.JDALogger;
import org.slf4j.Logger;

import javax.annotation.Nonnull;
import java.lang.reflect.InvocationTargetException;
Expand Down Expand Up @@ -51,15 +53,23 @@
*/
public class AnnotatedEventManager implements IEventManager
{
private static final Logger LOGGER = JDALogger.getLog(AnnotatedEventManager.class);
private final Set<Object> listeners = ConcurrentHashMap.newKeySet();
private final Map<Class<?>, Map<Object, List<Method>>> methods = new ConcurrentHashMap<>();

@Override
public void register(@Nonnull Object listener)
{
if (listener.getClass().isArray())
{
for (Object o : ((Object[]) listener))
register(o);
return;
}
freya022 marked this conversation as resolved.
Show resolved Hide resolved

if (listeners.add(listener))
{
updateMethods();
registerListenerMethods(listener);
}
}

Expand Down Expand Up @@ -114,32 +124,34 @@ private void updateMethods()
methods.clear();
for (Object listener : listeners)
{
boolean isClass = listener instanceof Class;
Class<?> c = isClass ? (Class) listener : listener.getClass();
Method[] allMethods = c.getDeclaredMethods();
for (Method m : allMethods)
{
if (!m.isAnnotationPresent(SubscribeEvent.class) || (isClass && !Modifier.isStatic(m.getModifiers())))
{
continue;
}
Class<?>[] pType = m.getParameterTypes();
if (pType.length == 1 && GenericEvent.class.isAssignableFrom(pType[0]))
{
Class<?> eventClass = pType[0];
if (!methods.containsKey(eventClass))
{
methods.put(eventClass, new ConcurrentHashMap<>());
}
registerListenerMethods(listener);
}
}

if (!methods.get(eventClass).containsKey(listener))
{
methods.get(eventClass).put(listener, new CopyOnWriteArrayList<>());
}
private void registerListenerMethods(Object listener)
{
boolean isClass = listener instanceof Class;
Class<?> c = isClass ? (Class<?>) listener : listener.getClass();
Method[] allMethods = c.getDeclaredMethods();
for (Method m : allMethods)
{
if (!m.isAnnotationPresent(SubscribeEvent.class))
continue;
//Skip member methods if listener is a Class
if (isClass && !Modifier.isStatic(m.getModifiers()))
continue;

methods.get(eventClass).get(listener).add(m);
}
final Class<?>[] parameterTypes = m.getParameterTypes();
if (parameterTypes.length != 1 || !GenericEvent.class.isAssignableFrom(parameterTypes[0]))
{
LOGGER.warn("Method '{}' annotated with @{} must have at most 1 parameter, which implements GenericEvent", m, SubscribeEvent.class.getSimpleName());
continue;
}

Class<?> eventClass = parameterTypes[0];
methods.computeIfAbsent(eventClass, k -> new ConcurrentHashMap<>())
.computeIfAbsent(listener, k -> new CopyOnWriteArrayList<>())
.add(m);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ protected JDAImpl buildInstance(final int shardId)
if (this.sessionConfig.getAudioSendFactory() != null)
jda.setAudioSendFactory(this.sessionConfig.getAudioSendFactory());

this.eventConfig.getListeners().forEach(jda::addEventListener);
jda.addEventListener(this.eventConfig.getListeners().toArray());
this.eventConfig.getListenerProviders().forEach(provider -> jda.addEventListener(provider.apply(shardId)));

// Set the presence information before connecting to have the correct information ready when sending IDENTIFY
Expand Down