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
+ static final String NOTIFY_KEYSPACE_EVENTS = "notify-keyspace-events" ;
58
+
39
59
private static final Topic TOPIC_ALL_KEYEVENTS = new PatternTopic ("__keyevent@*" );
40
60
41
- private final RedisMessageListenerContainer listenerContainer ;
61
+ private final Logger logger = LoggerFactory .getLogger (getClass ());
62
+
63
+ private final RedisMessageListenerContainer messageListenerContainer ;
42
64
43
- private String keyspaceNotificationsConfigParameter = "EA " ;
65
+ private String keyspaceNotificationsConfigParameter = "" ;
44
66
45
67
/**
46
- * Creates new {@link KeyspaceEventMessageListener}.
68
+ * Creates a new {@link KeyspaceEventMessageListener}.
47
69
*
48
- * @param listenerContainer must not be {@literal null}.
70
+ * @param messageListenerContainer {@link RedisMessageListenerContainer} in which this listener will be registered;
71
+ * must not be {@literal null}.
49
72
*/
50
- public KeyspaceEventMessageListener (RedisMessageListenerContainer listenerContainer ) {
73
+ public KeyspaceEventMessageListener (RedisMessageListenerContainer messageListenerContainer ) {
51
74
52
- Assert .notNull (listenerContainer , "RedisMessageListenerContainer to run in must not be null" );
53
- this .listenerContainer = listenerContainer ;
75
+ Assert .notNull (messageListenerContainer , "RedisMessageListenerContainer to run in must not be null" );
76
+
77
+ this .messageListenerContainer = messageListenerContainer ;
78
+ }
79
+
80
+ /**
81
+ * Returns a reference to the configured {@link Logger}.
82
+ *
83
+ * @return a reference to the configured {@link Logger}.
84
+ */
85
+ protected Logger getLogger () {
86
+ return this .logger ;
87
+ }
88
+
89
+ /**
90
+ * Returns a configured reference to the {@link RedisMessageListenerContainer} to which this {@link MessageListener}
91
+ * is registered.
92
+ *
93
+ * @return a configured reference to the {@link RedisMessageListenerContainer} to which this {@link MessageListener}
94
+ * is registered.
95
+ */
96
+ protected RedisMessageListenerContainer getMessageListenerContainer () {
97
+ return this .messageListenerContainer ;
54
98
}
55
99
56
100
@ Override
57
101
public void onMessage (Message message , @ Nullable byte [] pattern ) {
58
102
59
- if (ObjectUtils . isEmpty (message . getChannel ()) || ObjectUtils . isEmpty ( message . getBody () )) {
60
- return ;
103
+ if (containsChannelContent (message )) {
104
+ doHandleMessage ( message ) ;
61
105
}
106
+ }
62
107
63
- doHandleMessage (message );
108
+ // Message must have a channel and body (contain content)
109
+ private boolean containsChannelContent (Message message ) {
110
+ return !(ObjectUtils .isEmpty (message .getChannel ()) || ObjectUtils .isEmpty (message .getBody ()));
64
111
}
65
112
66
113
/**
67
- * Handle the actual message
114
+ * Handle the actual {@link Message}.
68
115
*
69
- * @param message never {@literal null}.
116
+ * @param message {@link Message} to process; never {@literal null}.
70
117
*/
71
118
protected abstract void doHandleMessage (Message message );
72
119
120
+ @ Override
121
+ public void afterPropertiesSet () throws Exception {
122
+ init ();
123
+ }
124
+
73
125
/**
74
- * Initialize the message listener by writing requried redis config for {@literal notify-keyspace-events} and
75
- * registering the listener within the container.
126
+ * Initialize this {@link MessageListener} by writing required Redis server config
127
+ * for {@literal notify-keyspace-events} and registering this {@link MessageListener}
128
+ * with the {@link RedisMessageListenerContainer}.
76
129
*/
77
130
public void init () {
78
131
79
- if ( StringUtils . hasText ( keyspaceNotificationsConfigParameter )) {
132
+ String keyspaceNotificationsConfigParameter = getKeyspaceNotificationsConfigParameter ();
80
133
81
- RedisConnection connection = listenerContainer .getConnectionFactory ().getConnection ();
134
+ if (isSet (keyspaceNotificationsConfigParameter )) {
135
+ configureKeyspaceEventNotifications (keyspaceNotificationsConfigParameter );
136
+ }
137
+
138
+ doRegister (getMessageListenerContainer ());
139
+ }
82
140
83
- try {
141
+ private boolean isSet (@ Nullable String value ) {
142
+ return StringUtils .hasText (value );
143
+ }
84
144
85
- Properties config = connection . getConfig ( "notify-keyspace-events" );
145
+ void configureKeyspaceEventNotifications ( String keyspaceNotificationsConfigParameter ) {
86
146
87
- if (!StringUtils .hasText (config .getProperty ("notify-keyspace-events" ))) {
88
- connection .setConfig ("notify-keyspace-events" , keyspaceNotificationsConfigParameter );
89
- }
147
+ RedisConnectionFactory connectionFactory = getMessageListenerContainer ().getConnectionFactory ();
90
148
91
- } finally {
92
- connection .close ();
149
+ if (connectionFactory != null ) {
150
+ try (RedisConnection connection = connectionFactory .getConnection ()) {
151
+ if (canChangeNotifyKeyspaceEvents (connection )) {
152
+ setKeyspaceEventNotifications (connection , keyspaceNotificationsConfigParameter );
153
+ }
93
154
}
94
155
}
156
+ else {
157
+ if (getLogger ().isWarnEnabled ()) {
158
+ getLogger ().warn ("Unable to configure notification on keyspace events;"
159
+ + " no RedisConnectionFactory was configured in the RedisMessageListenerContainer" );
160
+ }
161
+ }
162
+ }
163
+
164
+ private boolean canChangeNotifyKeyspaceEvents (@ Nullable RedisConnection connection ) {
165
+
166
+ if (connection != null ) {
167
+
168
+ Properties config = connection .serverCommands ().getConfig (NOTIFY_KEYSPACE_EVENTS );
169
+
170
+ return config == null || !isSet (config .getProperty (NOTIFY_KEYSPACE_EVENTS ));
171
+ }
172
+
173
+ return false ;
174
+ }
175
+
176
+ void setKeyspaceEventNotifications (RedisConnection connection , String keyspaceNotificationsConfigParameter ) {
177
+ connection .serverCommands ().setConfig (NOTIFY_KEYSPACE_EVENTS , keyspaceNotificationsConfigParameter );
178
+ }
95
179
96
- doRegister (listenerContainer );
180
+ @ Override
181
+ public void destroy () throws Exception {
182
+ getMessageListenerContainer ().removeMessageListener (this );
97
183
}
98
184
99
185
/**
100
- * Register instance within the container .
186
+ * Register instance within the {@link RedisMessageListenerContainer} .
101
187
*
102
188
* @param container never {@literal null}.
103
189
*/
104
190
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 );
191
+ container .addMessageListener (this , TOPIC_ALL_KEYEVENTS );
111
192
}
112
193
113
194
/**
114
- * Set the configuration string to use for {@literal notify-keyspace-events}.
195
+ * Set the {@link String configuration setting} (for example: {@literal EA}) to use
196
+ * for {@literal notify-keyspace-events}.
115
197
*
116
198
* @param keyspaceNotificationsConfigParameter can be {@literal null}.
117
199
* @since 1.8
@@ -120,8 +202,13 @@ public void setKeyspaceNotificationsConfigParameter(String keyspaceNotifications
120
202
this .keyspaceNotificationsConfigParameter = keyspaceNotificationsConfigParameter ;
121
203
}
122
204
123
- @ Override
124
- public void afterPropertiesSet () throws Exception {
125
- init ();
205
+ /**
206
+ * Get the configured {@link String setting} for {@literal notify-keyspace-events}.
207
+ *
208
+ * @return the configured {@link String setting} for {@literal notify-keyspace-events}.
209
+ */
210
+ @ Nullable
211
+ protected String getKeyspaceNotificationsConfigParameter () {
212
+ return this .keyspaceNotificationsConfigParameter ;
126
213
}
127
214
}
0 commit comments