View Javadoc

1   /***
2    *  Copyright 2003-2010 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.net.InetAddress;
20  import java.net.UnknownHostException;
21  import java.util.Collection;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Set;
25  import java.util.concurrent.CopyOnWriteArrayList;
26  
27  import net.sf.ehcache.CacheException;
28  import net.sf.ehcache.cluster.CacheCluster;
29  import net.sf.ehcache.cluster.ClusterNode;
30  import net.sf.ehcache.cluster.ClusterScheme;
31  import net.sf.ehcache.cluster.ClusterTopologyListener;
32  
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  
36  /***
37   * {@link CacheCluster} implementation that delegates to an underlying cache cluster. The underlying {@link CacheCluster} can be changed
38   * dynamically
39   *
40   * @author Abhishek Sanoujam
41   *
42   */
43  public class TerracottaCacheCluster implements CacheCluster {
44  
45      private static final Logger LOGGER = LoggerFactory.getLogger(TerracottaCacheCluster.class);
46      private final List<ClusterTopologyListener> listeners = new CopyOnWriteArrayList<ClusterTopologyListener>();
47      private volatile CacheCluster realCacheCluster;
48  
49      /***
50       * Set the underlying cache
51       *
52       * @param newCacheCluster
53       */
54      public void setUnderlyingCacheCluster(CacheCluster newCacheCluster) {
55          if (newCacheCluster == null) {
56              throw new IllegalArgumentException("CacheCluster can't be null");
57          }
58          final CacheCluster oldRealCacheCluster = this.realCacheCluster;
59          this.realCacheCluster = newCacheCluster;
60          for (ClusterTopologyListener listener : listeners) {
61              this.realCacheCluster.addTopologyListener(listener);
62          }
63          if (oldRealCacheCluster != null) {
64              for (ClusterTopologyListener listener : listeners) {
65                  oldRealCacheCluster.removeTopologyListener(listener);
66              }
67          }
68      }
69  
70      /***
71       * Fire Rejoin event to all listeners.
72       * Package protected method
73       *
74       * @param clusterNode
75       * @param oldNode
76       */
77      void fireNodeRejoinedEvent(ClusterNode oldNode, ClusterNode newNode) {
78          Set<ClusterTopologyListener> firedToListeners = new HashSet<ClusterTopologyListener>();
79          for (ClusterTopologyListener listener : realCacheCluster.getTopologyListeners()) {
80              firedToListeners.add(listener);
81              fireRejoinEvent(oldNode, newNode, listener);
82          }
83          for (ClusterTopologyListener listener : listeners) {
84              if (firedToListeners.contains(listener)) {
85                  continue;
86              }
87              fireRejoinEvent(oldNode, newNode, listener);
88          }
89      }
90  
91      private void fireRejoinEvent(ClusterNode oldNode, ClusterNode newNode, ClusterTopologyListener listener) {
92          try {
93              listener.clusterRejoined(new DisconnectedClusterNode(oldNode), newNode);
94          } catch (Throwable e) {
95              LOGGER.error("Caught exception while firing rejoin event", e);
96          }
97      }
98  
99      /***
100      * {@inheritDoc}
101      */
102     public boolean addTopologyListener(ClusterTopologyListener listener) {
103         checkIfInitialized();
104         boolean added = realCacheCluster.addTopologyListener(listener);
105         if (added) {
106             listeners.add(listener);
107         }
108         return added;
109     }
110 
111     /***
112      * {@inheritDoc}
113      */
114     public boolean removeTopologyListener(ClusterTopologyListener listener) {
115         checkIfInitialized();
116         boolean removed = realCacheCluster.removeTopologyListener(listener);
117         if (removed) {
118             listeners.remove(listener);
119         }
120         return removed;
121     }
122 
123     /***
124      * {@inheritDoc}
125      */
126     public ClusterNode getCurrentNode() {
127         checkIfInitialized();
128         return realCacheCluster.getCurrentNode();
129     }
130 
131     /***
132      * {@inheritDoc}
133      */
134     public Collection<ClusterNode> getNodes() {
135         checkIfInitialized();
136         return realCacheCluster.getNodes();
137     }
138 
139     /***
140      * {@inheritDoc}
141      */
142     public ClusterScheme getScheme() {
143         checkIfInitialized();
144         return realCacheCluster.getScheme();
145     }
146 
147     /***
148      * {@inheritDoc}
149      */
150     public boolean isClusterOnline() {
151         checkIfInitialized();
152         return realCacheCluster.isClusterOnline();
153     }
154 
155     /***
156      * {@inheritDoc}
157      */
158     public ClusterNode waitUntilNodeJoinsCluster() {
159         checkIfInitialized();
160         return realCacheCluster.waitUntilNodeJoinsCluster();
161     }
162 
163     private void checkIfInitialized() {
164         if (realCacheCluster == null) {
165             throw new CacheException(
166                     "The underlying cache cluster has not been initialized. Probably the terracotta client has not been configured yet.");
167         }
168     }
169 
170     /***
171      * Until fixed in terracotta core, getHostName() and getIp() does not work after node has disconnected.
172      * Temporary fix for beta release.
173      *
174      */
175     private static class DisconnectedClusterNode implements ClusterNode {
176 
177         private final ClusterNode delegateNode;
178 
179         /***
180          * Constructor accepting the actual delegate node
181          *
182          * @param actualNode
183          */
184         public DisconnectedClusterNode(final ClusterNode actualNode) {
185             this.delegateNode = actualNode;
186         }
187 
188         /***
189          * {@inheritDoc}
190          */
191         public String getId() {
192             return delegateNode.getId();
193         }
194 
195         /***
196          * {@inheritDoc}
197          */
198         public String getHostname() {
199             String hostName = "";
200             try {
201                 hostName = InetAddress.getLocalHost().getHostName();
202             } catch (UnknownHostException e) {
203                 hostName = "[Can't determine hostname and " + delegateNode.getId() + " has DISCONNECTED]";
204             }
205             return hostName;
206         }
207 
208         /***
209          * {@inheritDoc}
210          */
211         public String getIp() {
212             String ip = "";
213             try {
214                 ip = InetAddress.getLocalHost().getHostAddress();
215             } catch (UnknownHostException e) {
216                 ip = "[Can't determine IP and " + delegateNode.getId() + " has DISCONNECTED]";
217             }
218             return ip;
219         }
220 
221     }
222 
223     /***
224      * {@inheritDoc}
225      */
226     public List<ClusterTopologyListener> getTopologyListeners() {
227         return this.listeners;
228     }
229 
230 }