View Javadoc
1   /*
2    * *************************************************************************************************************************************************************
3    *
4    * TheseFoolishThings: Miscellaneous utilities
5    * http://tidalwave.it/projects/thesefoolishthings
6    *
7    * Copyright (C) 2009 - 2025 by Tidalwave s.a.s. (http://tidalwave.it)
8    *
9    * *************************************************************************************************************************************************************
10   *
11   * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12   * You may obtain a copy of the License at
13   *
14   *     http://www.apache.org/licenses/LICENSE-2.0
15   *
16   * 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
17   * CONDITIONS OF ANY KIND, either express or implied.  See the License for the specific language governing permissions and limitations under the License.
18   *
19   * *************************************************************************************************************************************************************
20   *
21   * git clone https://bitbucket.org/tidalwave/thesefoolishthings-src
22   * git clone https://github.com/tidalwave-it/thesefoolishthings-src
23   *
24   * *************************************************************************************************************************************************************
25   */
26  package it.tidalwave.messagebus;
27  
28  import java.lang.reflect.Method;
29  import javax.annotation.Nonnull;
30  import java.util.ArrayList;
31  import java.util.List;
32  import it.tidalwave.messagebus.annotation.ListensTo;
33  import it.tidalwave.messagebus.annotation.SimpleMessageSubscriber;
34  import lombok.RequiredArgsConstructor;
35  import lombok.extern.slf4j.Slf4j;
36  import static it.tidalwave.messagebus.spi.ReflectionUtils.*;
37  import static it.tidalwave.messagebus.spi.ReflectionUtils.MethodProcessor.FilterResult.*;
38  
39  /***************************************************************************************************************************************************************
40   *
41   * @author  Fabrizio Giudici
42   *
43   **************************************************************************************************************************************************************/
44  @RequiredArgsConstructor @Slf4j
45  public class MessageBusHelper
46    {
47      /***********************************************************************************************************************************************************
48       *
49       **********************************************************************************************************************************************************/
50      public static interface Adapter
51        {
52          @Nonnull
53          public <T> MethodAdapter<T> createMethodAdapter (@Nonnull Object object,
54                                                           @Nonnull Method method,
55                                                           @Nonnull Class<T> topic);
56  
57          public void publish (@Nonnull Object message);
58  
59          public <T> void publish (Class<T> topic, @Nonnull T message);
60        }
61  
62      /***********************************************************************************************************************************************************
63       *
64       **********************************************************************************************************************************************************/
65      public static interface MethodAdapter<T>
66        {
67          public void subscribe();
68  
69          public void unsubscribe();
70        }
71  
72      @Nonnull
73      private final Object owner;
74  
75      @Nonnull
76      private final Adapter methodAdapterFactory;
77  
78      private final List<MethodAdapter<?>> methodAdapters = new ArrayList<>();
79  
80      /***********************************************************************************************************************************************************
81       *
82       **********************************************************************************************************************************************************/
83      public void subscribeAll()
84        {
85          forEachMethodInTopDownHierarchy(owner, new MethodProcessor()
86            {
87              @Override @Nonnull
88              public FilterResult filter (@Nonnull final Class<?> clazz)
89                {
90                  return clazz.getAnnotation(SimpleMessageSubscriber.class) != null ? ACCEPT : IGNORE;
91                }
92  
93              @Override
94              public void process (@Nonnull final Method method)
95                {
96                  final var parameterAnnotations = method.getParameterAnnotations();
97  
98                  if ((parameterAnnotations.length == 1) && containsAnnotation(parameterAnnotations[0], ListensTo.class))
99                    {
100                     registerMessageListener(method);
101                   }
102               }
103           });
104       }
105 
106     /***********************************************************************************************************************************************************
107      *
108      **********************************************************************************************************************************************************/
109     public void unsubscribeAll()
110       {
111         for (final var methodAdapter : methodAdapters)
112           {
113             methodAdapter.unsubscribe();
114           }
115       }
116 
117     /***********************************************************************************************************************************************************
118      * Publishes a message.
119      *
120      * @param message     the message to deliver
121      **********************************************************************************************************************************************************/
122     public void publish (@Nonnull final Object message)
123       {
124         methodAdapterFactory.publish(message);
125       }
126 
127     /***********************************************************************************************************************************************************
128      * Publishes a message.
129      *
130      * @param <T>           the static type of the topic
131      * @param topicType     the dynamic type of the topic
132      * @param topic         the topic
133      **********************************************************************************************************************************************************/
134     public <T> void publish (@Nonnull final Class<T> topicType, @Nonnull final T topic)
135       {
136         methodAdapterFactory.publish(topicType, topic);
137       }
138 
139     /***********************************************************************************************************************************************************
140      * 
141      **********************************************************************************************************************************************************/
142     private <T> void registerMessageListener (@Nonnull final Method method)
143       {
144         log.trace("registerMessageListener({})", method);
145 
146         final var topic = (Class<T>)method.getParameterTypes()[0];
147         final var methodAdapter = methodAdapterFactory.createMethodAdapter(owner, method, topic);
148         methodAdapters.add(methodAdapter);
149         methodAdapter.subscribe();
150       }
151   }