1 /*
2 * *************************************************************************************************************************************************************
3 *
4 * TheseFoolishThings: Miscellaneous utilities
5 * http://tidalwave.it/projects/thesefoolishthings
6 *
7 * Copyright (C) 2009 - 2024 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 }