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 }