View Javadoc
1   package com.atlassian.plugin.event.impl;
2   
3   import com.atlassian.event.api.EventListener;
4   import com.atlassian.event.spi.ListenerHandler;
5   import com.atlassian.event.spi.ListenerInvoker;
6   import com.google.common.base.Function;
7   import com.google.common.collect.Iterables;
8   import com.google.common.collect.Lists;
9   import com.google.common.collect.Sets;
10  
11  import java.lang.reflect.InvocationTargetException;
12  import java.lang.reflect.Method;
13  import java.util.List;
14  import java.util.Optional;
15  import java.util.Set;
16  
17  import static com.google.common.base.Preconditions.checkNotNull;
18  
19  /**
20   * This class is used internally by the {@link DefaultPluginEventManager} to adapt {@link ListenerMethodSelector}s to
21   * the {@code ListenerHandler} interface used in Atlassian Event.
22   *
23   * @since 2.5.0
24   */
25  final class MethodSelectorListenerHandler implements ListenerHandler {
26      private final ListenerMethodSelector listenerMethodSelector;
27  
28      public MethodSelectorListenerHandler(ListenerMethodSelector listenerMethodSelector) {
29          this.listenerMethodSelector = listenerMethodSelector;
30      }
31  
32      public List<? extends ListenerInvoker> getInvokers(final Object listener) {
33          final List<Method> validMethods = getValidMethods(checkNotNull(listener));
34  
35          return Lists.newArrayList(Iterables.transform(validMethods, new Function<Method, ListenerInvoker>() {
36              public ListenerInvoker apply(final Method method) {
37                  return new ListenerInvoker() {
38                      @Override
39                      public Set<Class<?>> getSupportedEventTypes() {
40                          return Sets.newHashSet(method.getParameterTypes());
41                      }
42  
43                      @Override
44                      public void invoke(Object event) {
45                          try {
46                              method.invoke(listener, event);
47                          } catch (IllegalAccessException e) {
48                              throw new RuntimeException(e);
49                          } catch (InvocationTargetException e) {
50                              if (e.getCause() == null) {
51                                  throw new RuntimeException(e);
52                              } else if (e.getCause().getMessage() == null) {
53                                  throw new RuntimeException(e.getCause());
54                              } else {
55                                  throw new RuntimeException(e.getCause().getMessage(), e);
56                              }
57                          }
58                      }
59  
60                      @Override
61                      public Optional<String> getScope() {
62                          final EventListener annotation = method.getAnnotation(EventListener.class);
63                          return Optional
64                                  .ofNullable(annotation)
65                                  .map(EventListener::scope)
66                                  .filter(scopeName -> !"".equals(scopeName));
67                      }
68  
69                      @Override
70                      public boolean supportAsynchronousEvents() {
71                          return true;
72                      }
73                  };
74              }
75          }));
76      }
77  
78      private List<Method> getValidMethods(Object listener) {
79          final List<Method> listenerMethods = Lists.newArrayList();
80          for (Method method : listener.getClass().getMethods()) {
81              if (isValidMethod(method)) {
82                  listenerMethods.add(method);
83              }
84          }
85          return listenerMethods;
86      }
87  
88      private boolean isValidMethod(Method method) {
89          if (listenerMethodSelector.isListenerMethod(method)) {
90              if (hasOneAndOnlyOneParameter(method)) {
91                  return true;
92              } else {
93                  throw new RuntimeException("Method <" + method + "> of class <" + method.getDeclaringClass() + "> " +
94                          "is being registered as a listener but has 0 or more than 1 parameters! " +
95                          "Listener methods MUST have 1 and only 1 parameter.");
96              }
97          }
98          return false;
99      }
100 
101     private boolean hasOneAndOnlyOneParameter(Method method) {
102         return method.getParameterTypes().length == 1;
103     }
104 }