26
26
import java .util .concurrent .TimeUnit ;
27
27
import java .util .concurrent .atomic .AtomicReference ;
28
28
29
+ import org .apache .commons .logging .Log ;
30
+ import org .apache .commons .logging .LogFactory ;
31
+
29
32
import org .springframework .beans .BeansException ;
30
33
import org .springframework .beans .factory .InitializingBean ;
31
34
import org .springframework .context .ApplicationContext ;
32
35
import org .springframework .context .ApplicationContextAware ;
33
36
import org .springframework .context .ApplicationEventPublisher ;
34
37
import org .springframework .context .ApplicationListener ;
38
+ import org .springframework .context .SmartLifecycle ;
35
39
import org .springframework .core .convert .ConversionService ;
36
40
import org .springframework .data .keyvalue .core .AbstractKeyValueAdapter ;
37
41
import org .springframework .data .keyvalue .core .KeyValueAdapter ;
99
103
* @author Mark Paluch
100
104
* @author Andrey Muchnik
101
105
* @author John Blum
102
- * @author Lucian Torje
103
106
* @since 1.7
104
107
*/
105
108
public class RedisKeyValueAdapter extends AbstractKeyValueAdapter
106
- implements InitializingBean , ApplicationContextAware , ApplicationListener <RedisKeyspaceEvent > {
109
+ implements InitializingBean , SmartLifecycle , ApplicationContextAware , ApplicationListener <RedisKeyspaceEvent > {
107
110
108
111
/**
109
112
* Time To Live in seconds that phantom keys should live longer than the actual key.
110
113
*/
111
114
private static final int PHANTOM_KEY_TTL = 300 ;
112
115
116
+ private final Log logger = LogFactory .getLog (getClass ());
117
+ private final AtomicReference <State > state = new AtomicReference <>(State .CREATED );
118
+
113
119
private RedisOperations <?, ?> redisOps ;
114
120
private RedisConverter converter ;
115
121
private @ Nullable RedisMessageListenerContainer messageListenerContainer ;
@@ -121,6 +127,13 @@ public class RedisKeyValueAdapter extends AbstractKeyValueAdapter
121
127
private @ Nullable String keyspaceNotificationsConfigParameter = null ;
122
128
private ShadowCopy shadowCopy = ShadowCopy .DEFAULT ;
123
129
130
+ /**
131
+ * Lifecycle state of this factory.
132
+ */
133
+ enum State {
134
+ CREATED , STARTING , STARTED , STOPPING , STOPPED , DESTROYED ;
135
+ }
136
+
124
137
/**
125
138
* Creates new {@link RedisKeyValueAdapter} with default {@link RedisMappingContext} and default
126
139
* {@link RedisCustomConversions}.
@@ -202,7 +215,7 @@ public Object put(Object id, Object item, String keyspace) {
202
215
&& this .expirationListener .get () == null ) {
203
216
204
217
if (rdo .getTimeToLive () != null && rdo .getTimeToLive () > 0 ) {
205
- initKeyExpirationListener ();
218
+ initKeyExpirationListener (this . messageListenerContainer );
206
219
}
207
220
}
208
221
@@ -686,6 +699,11 @@ public void setShadowCopy(ShadowCopy shadowCopy) {
686
699
this .shadowCopy = shadowCopy ;
687
700
}
688
701
702
+ @ Override
703
+ public boolean isRunning () {
704
+ return State .STARTED .equals (this .state .get ());
705
+ }
706
+
689
707
/**
690
708
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
691
709
* @since 1.8
@@ -696,22 +714,61 @@ public void afterPropertiesSet() {
696
714
if (this .managedListenerContainer ) {
697
715
initMessageListenerContainer ();
698
716
}
717
+ }
718
+
719
+ @ Override
720
+ public void start () {
721
+
722
+ State current = this .state .getAndUpdate (state -> isCreatedOrStopped (state ) ? State .STARTING : state );
723
+
724
+ if (isCreatedOrStopped (current )) {
725
+
726
+ messageListenerContainer .start ();
727
+
728
+ if (ObjectUtils .nullSafeEquals (EnableKeyspaceEvents .ON_STARTUP , this .enableKeyspaceEvents )) {
729
+ initKeyExpirationListener (this .messageListenerContainer );
730
+ }
731
+
732
+ this .state .set (State .STARTED );
733
+ }
734
+ }
735
+
736
+ private static boolean isCreatedOrStopped (@ Nullable State state ) {
737
+ return State .CREATED .equals (state ) || State .STOPPED .equals (state );
738
+ }
739
+
740
+ @ Override
741
+ public void stop () {
699
742
700
- if (ObjectUtils .nullSafeEquals (EnableKeyspaceEvents .ON_STARTUP , this .enableKeyspaceEvents )) {
701
- initKeyExpirationListener ();
743
+ if (state .compareAndSet (State .STARTED , State .STOPPING )) {
744
+
745
+ KeyExpirationEventMessageListener listener = this .expirationListener .get ();
746
+ if (listener != null ) {
747
+
748
+ if (this .expirationListener .compareAndSet (listener , null )) {
749
+ try {
750
+ listener .destroy ();
751
+ } catch (Exception e ) {
752
+ logger .warn ("Could not destroy KeyExpirationEventMessageListener" , e );
753
+ }
754
+ }
755
+ }
756
+
757
+ messageListenerContainer .stop ();
758
+ state .set (State .STOPPED );
702
759
}
703
760
}
704
761
705
762
public void destroy () throws Exception {
706
763
707
- if (this .expirationListener .get () != null ) {
708
- this .expirationListener .get ().destroy ();
709
- }
764
+ stop ();
710
765
711
766
if (this .managedListenerContainer && this .messageListenerContainer != null ) {
712
767
this .messageListenerContainer .destroy ();
713
768
this .messageListenerContainer = null ;
714
769
}
770
+
771
+ this .state .set (State .DESTROYED );
715
772
}
716
773
717
774
@ Override
@@ -729,13 +786,12 @@ private void initMessageListenerContainer() {
729
786
this .messageListenerContainer = new RedisMessageListenerContainer ();
730
787
this .messageListenerContainer .setConnectionFactory (((RedisTemplate <?, ?>) redisOps ).getConnectionFactory ());
731
788
this .messageListenerContainer .afterPropertiesSet ();
732
- this .messageListenerContainer .start ();
733
789
}
734
790
735
- private void initKeyExpirationListener () {
791
+ private void initKeyExpirationListener (RedisMessageListenerContainer messageListenerContainer ) {
736
792
737
793
if (this .expirationListener .get () == null ) {
738
- MappingExpirationListener listener = new MappingExpirationListener (this . messageListenerContainer , this .redisOps ,
794
+ MappingExpirationListener listener = new MappingExpirationListener (messageListenerContainer , this .redisOps ,
739
795
this .converter , this .shadowCopy );
740
796
741
797
listener .setKeyspaceNotificationsConfigParameter (keyspaceNotificationsConfigParameter );
0 commit comments