1 /**
2  *  Copyright Terracotta, Inc.
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  */

16
17 package net.sf.ehcache.terracotta;
18
19 import java.util.Collection;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Set;
23 import java.util.concurrent.CopyOnWriteArrayList;
24
25 import net.sf.ehcache.CacheException;
26 import net.sf.ehcache.cluster.CacheCluster;
27 import net.sf.ehcache.cluster.ClusterNode;
28 import net.sf.ehcache.cluster.ClusterScheme;
29 import net.sf.ehcache.cluster.ClusterTopologyListener;
30
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 /**
35  * {@link CacheCluster} implementation that delegates to an underlying cache cluster. The underlying {@link CacheCluster} can be changed
36  * dynamically
37  *
38  * @author Abhishek Sanoujam
39  *
40  */

41 public class TerracottaCacheCluster implements CacheCluster {
42
43     private static final Logger LOGGER = LoggerFactory.getLogger(TerracottaCacheCluster.class);
44     private final List<ClusterTopologyListener> listeners = new CopyOnWriteArrayList<ClusterTopologyListener>();
45     private volatile CacheCluster realCacheCluster;
46
47     /**
48      * Set the underlying cache
49      *
50      * @param newCacheCluster
51      */

52     public void setUnderlyingCacheCluster(CacheCluster newCacheCluster) {
53         if (newCacheCluster == null) {
54             throw new IllegalArgumentException("CacheCluster can't be null");
55         }
56         final CacheCluster oldRealCacheCluster = this.realCacheCluster;
57         this.realCacheCluster = newCacheCluster;
58         for (ClusterTopologyListener listener : listeners) {
59             this.realCacheCluster.addTopologyListener(listener);
60         }
61         if (oldRealCacheCluster != null) {
62             for (ClusterTopologyListener listener : listeners) {
63                 oldRealCacheCluster.removeTopologyListener(listener);
64             }
65         }
66     }
67
68     /**
69      * Fire Rejoin event to all listeners.
70      * Package protected method
71      *
72      * @param oldNode
73      * @param newNode
74      */

75     void fireNodeRejoinedEvent(ClusterNode oldNode, ClusterNode newNode) {
76         Set<ClusterTopologyListener> firedToListeners = new HashSet<ClusterTopologyListener>();
77         for (ClusterTopologyListener listener : realCacheCluster.getTopologyListeners()) {
78             firedToListeners.add(listener);
79             fireRejoinEvent(oldNode, newNode, listener);
80         }
81         for (ClusterTopologyListener listener : listeners) {
82             if (firedToListeners.contains(listener)) {
83                 continue;
84             }
85             fireRejoinEvent(oldNode, newNode, listener);
86         }
87     }
88
89     private void fireRejoinEvent(ClusterNode oldNode, ClusterNode newNode, ClusterTopologyListener listener) {
90         try {
91             listener.clusterRejoined(oldNode, newNode);
92         } catch (Throwable e) {
93             LOGGER.error("Caught exception while firing rejoin event", e);
94         }
95     }
96
97     /**
98      * {@inheritDoc}
99      */

100     public boolean addTopologyListener(ClusterTopologyListener listener) {
101         checkIfInitialized();
102         boolean added = realCacheCluster.addTopologyListener(listener);
103         if (added) {
104             listeners.add(listener);
105         }
106         return added;
107     }
108
109     /**
110      * {@inheritDoc}
111      */

112     public boolean removeTopologyListener(ClusterTopologyListener listener) {
113         checkIfInitialized();
114         boolean removed = realCacheCluster.removeTopologyListener(listener);
115         if (removed) {
116             listeners.remove(listener);
117         }
118         return removed;
119     }
120
121     /**
122      * {@inheritDoc}
123      */

124     public ClusterNode getCurrentNode() {
125         checkIfInitialized();
126         return realCacheCluster.getCurrentNode();
127     }
128
129     /**
130      * {@inheritDoc}
131      */

132     public Collection<ClusterNode> getNodes() {
133         checkIfInitialized();
134         return realCacheCluster.getNodes();
135     }
136
137     /**
138      * {@inheritDoc}
139      */

140     public ClusterScheme getScheme() {
141         checkIfInitialized();
142         return realCacheCluster.getScheme();
143     }
144
145     /**
146      * {@inheritDoc}
147      */

148     public boolean isClusterOnline() {
149         checkIfInitialized();
150         return realCacheCluster.isClusterOnline();
151     }
152
153     /**
154      * {@inheritDoc}
155      */

156     public ClusterNode waitUntilNodeJoinsCluster() {
157         checkIfInitialized();
158         return realCacheCluster.waitUntilNodeJoinsCluster();
159     }
160
161     private void checkIfInitialized() {
162         if (realCacheCluster == null) {
163             throw new CacheException(
164                     "The underlying cache cluster has not been initialized. Probably the terracotta client has not been configured yet.");
165         }
166     }
167
168     /**
169      * {@inheritDoc}
170      */

171     public List<ClusterTopologyListener> getTopologyListeners() {
172         return this.listeners;
173     }
174
175 }
176