17
17
18
18
import java .util .Properties ;
19
19
20
+ import org .slf4j .Logger ;
21
+ import org .slf4j .LoggerFactory ;
20
22
import org .springframework .beans .factory .DisposableBean ;
21
23
import org .springframework .beans .factory .InitializingBean ;
22
24
import org .springframework .data .redis .connection .Message ;
23
25
import org .springframework .data .redis .connection .MessageListener ;
24
26
import org .springframework .data .redis .connection .RedisConnection ;
27
+ import org .springframework .data .redis .connection .RedisConnectionFactory ;
25
28
import org .springframework .lang .Nullable ;
26
29
import org .springframework .util .Assert ;
27
30
import org .springframework .util .ObjectUtils ;
28
31
import org .springframework .util .StringUtils ;
29
32
30
33
/**
31
34
* Base {@link MessageListener} implementation for listening to Redis keyspace notifications.
35
+ * <p>
36
+ * By default, this {@link MessageListener} does not listen for, or notify on, any keyspace events. You must explicitly
37
+ * set the {@link #setKeyspaceNotificationsConfigParameter(String)} to a valid {@literal redis.conf},
38
+ * {@literal notify-keyspace-events} value (for example: {@literal EA}) to enable keyspace event notifications
39
+ * from your Redis server.
40
+ * <p>
41
+ * Any configuration set in the Redis server take precedence. Therefore, if the Redis server already set a value
42
+ * for {@literal notify-keyspace-events}, then any {@link #setKeyspaceNotificationsConfigParameter(String)}
43
+ * specified on this listener will be ignored.
44
+ * <p>
45
+ * It is recommended that all infrastructure settings, such as {@literal notify-keyspace-events}, be configured on
46
+ * the Redis server itself. If the Redis server is rebooted, then any keyspace event configuration coming from
47
+ * the application will be lost when the Redis server is restarted since Redis server configuration is not persistent,
48
+ * and any configuration coming from your application only occurs during Spring container initialization.
32
49
*
33
50
* @author Christoph Strobl
34
51
* @author Mark Paluch
52
+ * @author John Blum
35
53
* @since 1.7
36
54
*/
37
55
public abstract class KeyspaceEventMessageListener implements MessageListener , InitializingBean , DisposableBean {
38
56
57
+ protected static final String DISABLED_KEY_EVENTS = "" ;
58
+ protected static final String NOTIFY_KEYSPACE_EVENTS = "notify-keyspace-events" ;
59
+
39
60
private static final Topic TOPIC_ALL_KEYEVENTS = new PatternTopic ("__keyevent@*" );
40
61
41
- private final RedisMessageListenerContainer listenerContainer ;
62
+ private final Logger logger = LoggerFactory .getLogger (getClass ());
63
+
64
+ private final RedisMessageListenerContainer messageListenerContainer ;
65
+
66
+ private @ Nullable String keyspaceNotificationsConfigParameter ;
67
+
68
+ /**
69
+ * Creates a new {@link KeyspaceEventMessageListener}.
70
+ *
71
+ * @param messageListenerContainer {@link RedisMessageListenerContainer} in which this listener will be registered;
72
+ * must not be {@literal null}.
73
+ */
74
+ public KeyspaceEventMessageListener (RedisMessageListenerContainer messageListenerContainer ) {
75
+ this (messageListenerContainer , DISABLED_KEY_EVENTS );
76
+ }
77
+
78
+ /**
79
+ * Creates a new {@link KeyspaceEventMessageListener} along with initialization for
80
+ * {@literal notify-keyspace-events}.
81
+ *
82
+ * @param messageListenerContainer {@link RedisMessageListenerContainer} in which this listener will be registered;
83
+ * must not be {@literal null}.
84
+ * @param keyspaceNotificationsConfigParameter {@link String default value} for {@literal notify-keyspace-events};
85
+ * may be {@literal null}.
86
+ */
87
+ protected KeyspaceEventMessageListener (RedisMessageListenerContainer messageListenerContainer ,
88
+ @ Nullable String keyspaceNotificationsConfigParameter ) {
89
+
90
+ Assert .notNull (messageListenerContainer , "RedisMessageListenerContainer to run in must not be null" );
42
91
43
- private String keyspaceNotificationsConfigParameter = "EA" ;
92
+ this .messageListenerContainer = messageListenerContainer ;
93
+ this .keyspaceNotificationsConfigParameter = keyspaceNotificationsConfigParameter ;
94
+ }
44
95
45
96
/**
46
- * Creates new {@link KeyspaceEventMessageListener }.
97
+ * Returns a reference to the configured {@link Logger }.
47
98
*
48
- * @param listenerContainer must not be {@literal null }.
99
+ * @return a reference to the configured {@link Logger }.
49
100
*/
50
- public KeyspaceEventMessageListener (RedisMessageListenerContainer listenerContainer ) {
101
+ protected Logger getLogger () {
102
+ return this .logger ;
103
+ }
51
104
52
- Assert .notNull (listenerContainer , "RedisMessageListenerContainer to run in must not be null" );
53
- this .listenerContainer = listenerContainer ;
105
+ /**
106
+ * Returns a configured reference to the {@link RedisMessageListenerContainer} to which this {@link MessageListener}
107
+ * is registered.
108
+ *
109
+ * @return a configured reference to the {@link RedisMessageListenerContainer} to which this {@link MessageListener}
110
+ * is registered.
111
+ */
112
+ protected RedisMessageListenerContainer getMessageListenerContainer () {
113
+ return this .messageListenerContainer ;
54
114
}
55
115
56
116
@ Override
57
117
public void onMessage (Message message , @ Nullable byte [] pattern ) {
58
118
59
- if (ObjectUtils . isEmpty (message . getChannel ()) || ObjectUtils . isEmpty ( message . getBody () )) {
60
- return ;
119
+ if (containsChannelContent (message )) {
120
+ doHandleMessage ( message ) ;
61
121
}
122
+ }
62
123
63
- doHandleMessage (message );
124
+ // Message must have a channel and body (contain content)
125
+ private boolean containsChannelContent (Message message ) {
126
+ return !(ObjectUtils .isEmpty (message .getChannel ()) || ObjectUtils .isEmpty (message .getBody ()));
64
127
}
65
128
66
129
/**
67
- * Handle the actual message
130
+ * Handle the actual {@link Message}.
68
131
*
69
- * @param message never {@literal null}.
132
+ * @param message {@link Message} to process; never {@literal null}.
70
133
*/
71
134
protected abstract void doHandleMessage (Message message );
72
135
136
+ @ Override
137
+ public void afterPropertiesSet () throws Exception {
138
+ init ();
139
+ }
140
+
73
141
/**
74
- * Initialize the message listener by writing requried redis config for {@literal notify-keyspace-events} and
75
- * registering the listener within the container.
142
+ * Initialize this {@link MessageListener} by writing required Redis server config
143
+ * for {@literal notify-keyspace-events} and registering this {@link MessageListener}
144
+ * with the {@link RedisMessageListenerContainer}.
76
145
*/
77
146
public void init () {
78
147
79
- if ( StringUtils . hasText ( keyspaceNotificationsConfigParameter )) {
148
+ String keyspaceNotificationsConfigParameter = getKeyspaceNotificationsConfigParameter ();
80
149
81
- RedisConnection connection = listenerContainer .getConnectionFactory ().getConnection ();
150
+ if (isSet (keyspaceNotificationsConfigParameter )) {
151
+ configureKeyspaceEventNotifications (keyspaceNotificationsConfigParameter );
152
+ }
82
153
83
- try {
154
+ doRegister (getMessageListenerContainer ());
155
+ }
84
156
85
- Properties config = connection .getConfig ("notify-keyspace-events" );
157
+ private boolean isSet (@ Nullable String value ) {
158
+ return StringUtils .hasText (value );
159
+ }
86
160
87
- if (! StringUtils . hasText ( config . getProperty ( "notify-keyspace-events" )) ) {
88
- connection . setConfig ( "notify-keyspace-events" , keyspaceNotificationsConfigParameter );
89
- }
161
+ void configureKeyspaceEventNotifications ( String keyspaceNotificationsConfigParameter ) {
162
+
163
+ RedisConnectionFactory connectionFactory = getMessageListenerContainer (). getConnectionFactory ();
90
164
91
- } finally {
92
- connection .close ();
165
+ if (connectionFactory != null ) {
166
+ try (RedisConnection connection = connectionFactory .getConnection ()) {
167
+ if (canChangeNotifyKeyspaceEvents (connection )) {
168
+ setKeyspaceEventNotifications (connection , keyspaceNotificationsConfigParameter );
169
+ }
93
170
}
94
171
}
172
+ else {
173
+ if (getLogger ().isWarnEnabled ()) {
174
+ getLogger ().warn ("Unable to configure notification on keyspace events;"
175
+ + " no RedisConnectionFactory was configured in the RedisMessageListenerContainer" );
176
+ }
177
+ }
178
+ }
179
+
180
+ private boolean canChangeNotifyKeyspaceEvents (@ Nullable RedisConnection connection ) {
181
+
182
+ if (connection != null ) {
183
+
184
+ Properties config = connection .serverCommands ().getConfig (NOTIFY_KEYSPACE_EVENTS );
185
+
186
+ return config == null || !isSet (config .getProperty (NOTIFY_KEYSPACE_EVENTS ));
187
+ }
95
188
96
- doRegister (listenerContainer );
189
+ return false ;
190
+ }
191
+
192
+ void setKeyspaceEventNotifications (RedisConnection connection , String keyspaceNotificationsConfigParameter ) {
193
+ connection .serverCommands ().setConfig (NOTIFY_KEYSPACE_EVENTS , keyspaceNotificationsConfigParameter );
194
+ }
195
+
196
+ @ Override
197
+ public void destroy () throws Exception {
198
+ getMessageListenerContainer ().removeMessageListener (this );
97
199
}
98
200
99
201
/**
100
- * Register instance within the container .
202
+ * Register instance within the {@link RedisMessageListenerContainer} .
101
203
*
102
204
* @param container never {@literal null}.
103
205
*/
104
206
protected void doRegister (RedisMessageListenerContainer container ) {
105
- listenerContainer .addMessageListener (this , TOPIC_ALL_KEYEVENTS );
106
- }
107
-
108
- @ Override
109
- public void destroy () throws Exception {
110
- listenerContainer .removeMessageListener (this );
207
+ container .addMessageListener (this , TOPIC_ALL_KEYEVENTS );
111
208
}
112
209
113
210
/**
114
- * Set the configuration string to use for {@literal notify-keyspace-events}.
211
+ * Set the {@link String configuration setting} (for example: {@literal EA}) to use
212
+ * for {@literal notify-keyspace-events}.
115
213
*
116
214
* @param keyspaceNotificationsConfigParameter can be {@literal null}.
117
215
* @since 1.8
@@ -120,8 +218,13 @@ public void setKeyspaceNotificationsConfigParameter(String keyspaceNotifications
120
218
this .keyspaceNotificationsConfigParameter = keyspaceNotificationsConfigParameter ;
121
219
}
122
220
123
- @ Override
124
- public void afterPropertiesSet () throws Exception {
125
- init ();
221
+ /**
222
+ * Get the configured {@link String setting} for {@literal notify-keyspace-events}.
223
+ *
224
+ * @return the configured {@link String setting} for {@literal notify-keyspace-events}.
225
+ */
226
+ @ Nullable
227
+ protected String getKeyspaceNotificationsConfigParameter () {
228
+ return this .keyspaceNotificationsConfigParameter ;
126
229
}
127
230
}
0 commit comments