001: /**
002: * $Revision$
003: * $Date$
004: *
005: * Copyright (C) 1999-2005 Jive Software. All rights reserved.
006: * This software is the proprietary information of Jive Software. Use is subject to license terms.
007: */package org.jivesoftware.util.cache;
008:
009: import org.jivesoftware.openfire.XMPPServer;
010: import org.jivesoftware.openfire.XMPPServerListener;
011: import org.jivesoftware.openfire.cluster.ClusterEventListener;
012: import org.jivesoftware.openfire.cluster.ClusterManager;
013: import org.jivesoftware.openfire.cluster.ClusterNodeInfo;
014: import org.jivesoftware.openfire.container.Plugin;
015: import org.jivesoftware.openfire.container.PluginClassLoader;
016: import org.jivesoftware.openfire.container.PluginManager;
017: import org.jivesoftware.util.InitializationException;
018: import org.jivesoftware.util.JiveConstants;
019: import org.jivesoftware.util.JiveGlobals;
020: import org.jivesoftware.util.Log;
021:
022: import java.util.*;
023: import java.util.concurrent.ConcurrentHashMap;
024:
025: /**
026: * Creates Cache objects. The returned caches will either be local or clustered
027: * depending on the clustering enabled setting and a user's license.<p>
028: * <p/>
029: * When clustered caching is turned on, cache usage statistics for all caches
030: * that have been created are periodically published to the clustered cache
031: * named "opt-$cacheStats".
032: *
033: */
034: public class CacheFactory {
035:
036: public static String LOCAL_CACHE_PROPERTY_NAME = "cache.clustering.local.class";
037: public static String CLUSTERED_CACHE_PROPERTY_NAME = "cache.clustering.clustered.class";
038:
039: private static boolean clusteringStarted = false;
040: private static boolean clusteringStarting = false;
041:
042: /**
043: * Storage for all caches that get created.
044: */
045: private static Map<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
046:
047: private static String localCacheFactoryClass;
048: private static String clusteredCacheFactoryClass;
049: private static CacheFactoryStrategy cacheFactoryStrategy;
050: private static Thread statsThread;
051:
052: public static final int DEFAULT_MAX_CACHE_SIZE = 1024 * 256;
053: public static final long DEFAULT_MAX_CACHE_LIFETIME = 6 * JiveConstants.HOUR;
054:
055: /**
056: * This map contains property names which were used to store cache configuration data
057: * in local xml properties in previous versions.
058: */
059: private static final Map<String, String> cacheNames = new HashMap<String, String>();
060: /**
061: * Default properties to use for local caches. Default properties can be overridden
062: * by setting the corresponding system properties.
063: */
064: private static final Map<String, Long> cacheProps = new HashMap<String, Long>();
065:
066: static {
067: localCacheFactoryClass = JiveGlobals
068: .getProperty(LOCAL_CACHE_PROPERTY_NAME,
069: "org.jivesoftware.util.cache.DefaultLocalCacheStrategy");
070: clusteredCacheFactoryClass = JiveGlobals
071: .getProperty(CLUSTERED_CACHE_PROPERTY_NAME,
072: "com.jivesoftware.util.cache.CoherenceClusteredCacheFactory");
073:
074: cacheNames.put("Favicon Hits", "faviconHits");
075: cacheNames.put("Favicon Misses", "faviconMisses");
076: cacheNames.put("Group", "group");
077: cacheNames.put("Group Metadata Cache", "groupMeta");
078: cacheNames.put("Javascript Cache", "javascript");
079: cacheNames.put("Last Activity Cache", "lastActivity");
080: cacheNames.put("Multicast Service", "multicast");
081: cacheNames.put("Offline Message Size", "offlinemessage");
082: cacheNames.put("Offline Presence Cache", "offlinePresence");
083: cacheNames.put("Privacy Lists", "listsCache");
084: cacheNames.put("Remote Users Existence", "remoteUsersCache");
085: cacheNames.put("Roster", "username2roster");
086: cacheNames.put("User", "userCache");
087: cacheNames.put("VCard", "vcardCache");
088: cacheNames.put("File Transfer Cache", "fileTransfer");
089: cacheNames.put("File Transfer", "transferProxy");
090: cacheNames.put("POP3 Authentication", "pop3");
091: cacheNames.put("LDAP Authentication", "ldap");
092: cacheNames.put("Routing Servers Cache", "routeServer");
093: cacheNames.put("Routing Components Cache", "routeComponent");
094: cacheNames.put("Routing Users Cache", "routeUser");
095: cacheNames.put("Routing AnonymousUsers Cache",
096: "routeAnonymousUser");
097: cacheNames.put("Routing User Sessions", "routeUserSessions");
098: cacheNames.put("Components Sessions", "componentsSessions");
099: cacheNames.put("Connection Managers Sessions",
100: "connManagerSessions");
101: cacheNames.put("Incoming Server Sessions", "incServerSessions");
102: cacheNames.put("Sessions by Hostname", "sessionsHostname");
103: cacheNames.put("Secret Keys Cache", "secretKeys");
104: cacheNames.put("Validated Domains", "validatedDomains");
105: cacheNames.put("Directed Presences", "directedPresences");
106: cacheNames.put("Disco Server Features", "serverFeatures");
107: cacheNames.put("Disco Server Items", "serverItems");
108: cacheNames.put("Remote Server Configurations",
109: "serversConfigurations");
110: cacheNames.put("Entity Capabilities", "entityCapabilities");
111: cacheNames.put("Entity Capabilities Users",
112: "entityCapabilitiesUsers");
113: cacheNames.put("Entity Capabilities Pending Hashes",
114: "entityCapabilitiesPendingHashes");
115:
116: cacheProps.put("cache.fileTransfer.size", 128 * 1024l);
117: cacheProps.put("cache.fileTransfer.maxLifetime",
118: 1000 * 60 * 10l);
119: cacheProps.put("cache.multicast.size", 128 * 1024l);
120: cacheProps
121: .put("cache.multicast.maxLifetime", JiveConstants.DAY);
122: cacheProps.put("cache.offlinemessage.size", 100 * 1024l);
123: cacheProps.put("cache.offlinemessage.maxLifetime",
124: JiveConstants.HOUR * 12);
125: cacheProps.put("cache.pop3.size", 512 * 1024l);
126: cacheProps.put("cache.pop3.maxLifetime", JiveConstants.HOUR);
127: cacheProps.put("cache.transferProxy.size", -1l);
128: cacheProps.put("cache.transferProxy.maxLifetime",
129: 1000 * 60 * 10l);
130: cacheProps.put("cache.group.size", 1024 * 1024l);
131: cacheProps.put("cache.group.maxLifetime",
132: JiveConstants.MINUTE * 15);
133: cacheProps.put("cache.groupMeta.size", 512 * 1024l);
134: cacheProps.put("cache.groupMeta.maxLifetime",
135: JiveConstants.MINUTE * 15);
136: cacheProps.put("cache.javascript.size", 128 * 1024l);
137: cacheProps.put("cache.javascript.maxLifetime", 3600 * 24 * 10l);
138: cacheProps.put("cache.ldap.size", 512 * 1024l);
139: cacheProps
140: .put("cache.ldap.maxLifetime", JiveConstants.HOUR * 2);
141: cacheProps.put("cache.listsCache.size", 512 * 1024l);
142: cacheProps.put("cache.offlinePresence.size", 512 * 1024l);
143: cacheProps.put("cache.lastActivity.size", 128 * 1024l);
144: cacheProps.put("cache.userCache.size", 512 * 1024l);
145: cacheProps.put("cache.userCache.maxLifetime",
146: JiveConstants.MINUTE * 30);
147: cacheProps.put("cache.remoteUsersCache.size", 512 * 1024l);
148: cacheProps.put("cache.remoteUsersCache.maxLifetime",
149: JiveConstants.MINUTE * 30);
150: cacheProps.put("cache.vcardCache.size", 512 * 1024l);
151: cacheProps.put("cache.faviconHits.size", 128 * 1024l);
152: cacheProps.put("cache.faviconMisses.size", 128 * 1024l);
153: cacheProps.put("cache.routeServer.size", -1l);
154: cacheProps.put("cache.routeServer.maxLifetime", -1l);
155: cacheProps.put("cache.routeComponent.size", -1l);
156: cacheProps.put("cache.routeComponent.maxLifetime", -1l);
157: cacheProps.put("cache.routeUser.size", -1l);
158: cacheProps.put("cache.routeUser.maxLifetime", -1l);
159: cacheProps.put("cache.routeAnonymousUser.size", -1l);
160: cacheProps.put("cache.routeAnonymousUser.maxLifetime", -1l);
161: cacheProps.put("cache.routeUserSessions.size", -1l);
162: cacheProps.put("cache.routeUserSessions.maxLifetime", -1l);
163: cacheProps.put("cache.componentsSessions.size", -1l);
164: cacheProps.put("cache.componentsSessions.maxLifetime", -1l);
165: cacheProps.put("cache.connManagerSessions.size", -1l);
166: cacheProps.put("cache.connManagerSessions.maxLifetime", -1l);
167: cacheProps.put("cache.incServerSessions.size", -1l);
168: cacheProps.put("cache.incServerSessions.maxLifetime", -1l);
169: cacheProps.put("cache.sessionsHostname.size", -1l);
170: cacheProps.put("cache.sessionsHostname.maxLifetime", -1l);
171: cacheProps.put("cache.secretKeys.size", -1l);
172: cacheProps.put("cache.secretKeys.maxLifetime", -1l);
173: cacheProps.put("cache.validatedDomains.size", -1l);
174: cacheProps.put("cache.validatedDomains.maxLifetime", -1l);
175: cacheProps.put("cache.directedPresences.size", -1l);
176: cacheProps.put("cache.directedPresences.maxLifetime", -1l);
177: cacheProps.put("cache.serverFeatures.size", -1l);
178: cacheProps.put("cache.serverFeatures.maxLifetime", -1l);
179: cacheProps.put("cache.serverItems.size", -1l);
180: cacheProps.put("cache.serverItems.maxLifetime", -1l);
181: cacheProps.put("cache.serversConfigurations.size", 128 * 1024l);
182: cacheProps.put("cache.serversConfigurations.maxLifetime",
183: JiveConstants.MINUTE * 30);
184: cacheProps.put("cache.entityCapabilities.size", -1l);
185: cacheProps.put("cache.entityCapabilities.maxLifetime",
186: JiveConstants.DAY * 2);
187: cacheProps.put("cache.entityCapabilitiesUsers.size", -1l);
188: cacheProps.put("cache.entityCapabilitiesUsers.maxLifetime",
189: JiveConstants.DAY * 2);
190: cacheProps.put("cache.entityCapabilitiesPendingHashes.size",
191: -1l);
192: cacheProps.put(
193: "cache.entityCapabilitiesPendingHashes.maxLifetime",
194: JiveConstants.DAY * 2);
195: cacheProps.put("cache.pluginCacheInfo.size", -1l);
196: cacheProps.put("cache.pluginCacheInfo.maxLifetime", -1l);
197: }
198:
199: private CacheFactory() {
200: }
201:
202: /**
203: * If a local property is found for the supplied name which specifies a value for cache size, it is returned.
204: * Otherwise, the defaultSize argument is returned.
205: *
206: * @param cacheName the name of the cache to look up a corresponding property for.
207: * @return either the property value or the default value.
208: */
209: public static long getMaxCacheSize(String cacheName) {
210: return getCacheProperty(cacheName, ".size",
211: DEFAULT_MAX_CACHE_SIZE);
212: }
213:
214: /**
215: * Sets a local property which overrides the maximum cache size as configured in coherence-cache-config.xml for the
216: * supplied cache name.
217: * @param cacheName the name of the cache to store a value for.
218: * @param size the maximum cache size.
219: */
220: public static void setMaxSizeProperty(String cacheName, long size) {
221: cacheName = cacheName.replaceAll(" ", "");
222: JiveGlobals.setProperty("cache." + cacheName + ".size", Long
223: .toString(size));
224: }
225:
226: public static boolean hasMaxSizeFromProperty(String cacheName) {
227: return hasCacheProperty(cacheName, ".size");
228: }
229:
230: /**
231: * If a local property is found for the supplied name which specifies a value for cache entry lifetime, it
232: * is returned. Otherwise, the defaultLifetime argument is returned.
233: *
234: * @param cacheName the name of the cache to look up a corresponding property for.
235: * @return either the property value or the default value.
236: */
237: public static long getMaxCacheLifetime(String cacheName) {
238: return getCacheProperty(cacheName, ".maxLifetime",
239: DEFAULT_MAX_CACHE_LIFETIME);
240: }
241:
242: /**
243: * Sets a local property which overrides the maximum cache entry lifetime as configured in coherence-cache-config.xml
244: * for the supplied cache name.
245: * @param cacheName the name of the cache to store a value for.
246: * @param lifetime the maximum cache entry lifetime.
247: */
248: public static void setMaxLifetimeProperty(String cacheName,
249: long lifetime) {
250: cacheName = cacheName.replaceAll(" ", "");
251: JiveGlobals.setProperty(
252: ("cache." + cacheName + ".maxLifetime"), Long
253: .toString(lifetime));
254: }
255:
256: public static boolean hasMaxLifetimeFromProperty(String cacheName) {
257: return hasCacheProperty(cacheName, ".maxLifetime");
258: }
259:
260: public static void setCacheTypeProperty(String cacheName,
261: String type) {
262: cacheName = cacheName.replaceAll(" ", "");
263: JiveGlobals.setProperty("cache." + cacheName + ".type", type);
264: }
265:
266: public static String getCacheTypeProperty(String cacheName) {
267: cacheName = cacheName.replaceAll(" ", "");
268: return JiveGlobals.getProperty("cache." + cacheName + ".type");
269: }
270:
271: public static void setMinCacheSize(String cacheName, long size) {
272: cacheName = cacheName.replaceAll(" ", "");
273: JiveGlobals.setProperty("cache." + cacheName + ".min", Long
274: .toString(size));
275: }
276:
277: public static long getMinCacheSize(String cacheName) {
278: return getCacheProperty(cacheName, ".min", 0);
279: }
280:
281: private static long getCacheProperty(String cacheName,
282: String suffix, long defaultValue) {
283: // First check if user is overwriting default value using a system property for the cache name
284: String propName = "cache." + cacheName.replaceAll(" ", "")
285: + suffix;
286: String sizeProp = JiveGlobals.getProperty(propName);
287: if (sizeProp == null && cacheNames.containsKey(cacheName)) {
288: // No system property was found for the cache name so try now with short name
289: propName = "cache." + cacheNames.get(cacheName) + suffix;
290: sizeProp = JiveGlobals.getProperty(propName);
291: }
292: if (sizeProp != null) {
293: try {
294: return Long.parseLong(sizeProp);
295: } catch (NumberFormatException nfe) {
296: Log.warn("Unable to parse " + propName
297: + " using default value.");
298: }
299: }
300: // Check if there is a default size value for this cache
301: Long defaultSize = cacheProps.get(propName);
302: return defaultSize == null ? defaultValue : defaultSize;
303: }
304:
305: private static boolean hasCacheProperty(String cacheName,
306: String suffix) {
307: // First check if user is overwriting default value using a system property for the cache name
308: String propName = "cache." + cacheName.replaceAll(" ", "")
309: + suffix;
310: String sizeProp = JiveGlobals.getProperty(propName);
311: if (sizeProp == null && cacheNames.containsKey(cacheName)) {
312: // No system property was found for the cache name so try now with short name
313: propName = "cache." + cacheNames.get(cacheName) + suffix;
314: sizeProp = JiveGlobals.getProperty(propName);
315: }
316: if (sizeProp != null) {
317: try {
318: Long.parseLong(sizeProp);
319: return true;
320: } catch (NumberFormatException nfe) {
321: Log.warn("Unable to parse " + propName
322: + " using default value.");
323: }
324: }
325: return false;
326: }
327:
328: /**
329: * Returns an array of all caches in the system.
330: * @return an array of all caches in the system.
331: */
332: public static Cache[] getAllCaches() {
333: List<Cache> values = new ArrayList<Cache>();
334: for (Cache cache : caches.values()) {
335: values.add(cache);
336: }
337: return values.toArray(new Cache[values.size()]);
338: }
339:
340: /**
341: * Returns the named cache, creating it as necessary.
342: *
343: * @param name the name of the cache to create.
344: * @return the named cache, creating it as necessary.
345: */
346: @SuppressWarnings("unchecked")
347: public static synchronized <T extends Cache> T createCache(
348: String name) {
349: T cache = (T) caches.get(name);
350: if (cache != null) {
351: return cache;
352: }
353:
354: cache = (T) cacheFactoryStrategy.createCache(name);
355:
356: return wrapCache(cache, name);
357: }
358:
359: /**
360: * Destroys the cache for the cache name specified.
361: *
362: * @param name the name of the cache to destroy.
363: */
364: public static void destroyCache(String name) {
365: Cache cache = caches.remove(name);
366: if (cache != null) {
367: cacheFactoryStrategy.destroyCache(cache);
368: }
369: }
370:
371: public static void lockKey(Object key, long timeout) {
372: cacheFactoryStrategy.lockKey(key, timeout);
373: }
374:
375: public static void unlockKey(Object key) {
376: cacheFactoryStrategy.unlockKey(key);
377: }
378:
379: @SuppressWarnings("unchecked")
380: private static <T extends Cache> T wrapCache(T cache, String name) {
381: cache = (T) new CacheWrapper(cache);
382: cache.setName(name);
383:
384: caches.put(name, cache);
385: return cache;
386: }
387:
388: /**
389: * Returns true if clustering is installed and can be used by this JVM
390: * to join a cluster. A false value could mean that either clustering
391: * support is not available or the license does not allow to have more
392: * than 1 cluster node.
393: *
394: * @return true if clustering is installed and can be used by
395: * this JVM to join a cluster.
396: */
397: public static boolean isClusteringAvailable() {
398: return getMaxClusterNodes() > 1;
399: }
400:
401: /**
402: * Returns true is clustering is currently being started. Once the cluster
403: * is started or failed to be started this value will be false.
404: *
405: * @return true is clustering is currently being started.
406: */
407: public static boolean isClusteringStarting() {
408: return clusteringStarting;
409: }
410:
411: /**
412: * Returns true if this node is currently a member of a cluster. The last step of application
413: * initialization is to join a cluster, so this method returns false during most of application startup.
414: *
415: * @return true if this node is currently a member of a cluster.
416: */
417: public static boolean isClusteringStarted() {
418: return clusteringStarted;
419: }
420:
421: /**
422: * Returns a byte[] that uniquely identifies this member within the cluster or <tt>null</tt>
423: * when not in a cluster.
424: *
425: * @return a byte[] that uniquely identifies this member within the cluster or null when not in a cluster.
426: */
427: public static byte[] getClusterMemberID() {
428: return cacheFactoryStrategy.getClusterMemberID();
429: }
430:
431: public synchronized static void clearCaches() {
432: for (String cacheName : caches.keySet()) {
433: Cache cache = caches.get(cacheName);
434: cache.clear();
435: }
436: }
437:
438: /**
439: * Returns a byte[] that uniquely identifies this senior cluster member or <tt>null</tt>
440: * when not in a cluster.
441: *
442: * @return a byte[] that uniquely identifies this senior cluster member or null when not in a cluster.
443: */
444: public static byte[] getSeniorClusterMemberID() {
445: return cacheFactoryStrategy.getSeniorClusterMemberID();
446: }
447:
448: /**
449: * Returns true if this member is the senior member in the cluster. If clustering
450: * is not enabled, this method will also return true. This test is useful for
451: * tasks that should only be run on a single member in a cluster.
452: *
453: * @return true if this cluster member is the senior or if clustering is not enabled.
454: */
455: public static boolean isSeniorClusterMember() {
456: return cacheFactoryStrategy.isSeniorClusterMember();
457: }
458:
459: /**
460: * Returns basic information about the current members of the cluster or an empty
461: * collection if not running in a cluster.
462: *
463: * @return information about the current members of the cluster or an empty
464: * collection if not running in a cluster.
465: */
466: public static Collection<ClusterNodeInfo> getClusterNodesInfo() {
467: return cacheFactoryStrategy.getClusterNodesInfo();
468: }
469:
470: /**
471: * Returns the maximum number of cluster members allowed. A value of 0 or 1 will
472: * be returned when clustering is not allowed.
473: *
474: * @return the maximum number of cluster members allowed or 0 or 1 if clustering is not allowed.
475: */
476: public static int getMaxClusterNodes() {
477: try {
478: CacheFactoryStrategy cacheFactory = (CacheFactoryStrategy) Class
479: .forName(
480: clusteredCacheFactoryClass,
481: true,
482: getClusteredCacheStrategyClassLoader("enterprise"))
483: .newInstance();
484: return cacheFactory.getMaxClusterNodes();
485: } catch (ClassNotFoundException e) {
486: // Do nothing
487: } catch (Exception e) {
488: Log.error("Error instantiating clustered cache factory", e);
489: }
490: return 0;
491: }
492:
493: /**
494: * Invokes a task on other cluster members in an asynchronous fashion. The task will not be
495: * executed on the local cluster member. If clustering is not enabled, this method
496: * will do nothing.
497: *
498: * @param task the task to be invoked on all other cluster members.
499: */
500: public static void doClusterTask(final ClusterTask task) {
501: cacheFactoryStrategy.doClusterTask(task);
502: }
503:
504: /**
505: * Invokes a task on a given cluster member in an asynchronous fashion. If clustering is not enabled,
506: * this method will do nothing.
507: *
508: * @param task the task to be invoked on the specified cluster member.
509: * @param nodeID the byte array that identifies the target cluster member.
510: * @throws IllegalStateException if requested node was not found or not running in a cluster.
511: */
512: public static void doClusterTask(final ClusterTask task,
513: byte[] nodeID) {
514: cacheFactoryStrategy.doClusterTask(task, nodeID);
515: }
516:
517: /**
518: * Invokes a task on other cluster members synchronously and returns the result as a Collection
519: * (method will not return until the task has been executed on each cluster member).
520: * The task will not be executed on the local cluster member. If clustering is not enabled,
521: * this method will return an empty collection.
522: *
523: * @param task the ClusterTask object to be invoked on all other cluster members.
524: * @param includeLocalMember true to run the task on the local member, false otherwise
525: * @return collection with the result of the execution.
526: */
527: public static Collection<Object> doSynchronousClusterTask(
528: ClusterTask task, boolean includeLocalMember) {
529: return cacheFactoryStrategy.doSynchronousClusterTask(task,
530: includeLocalMember);
531: }
532:
533: /**
534: * Invokes a task on a given cluster member synchronously and returns the result of
535: * the remote operation. If clustering is not enabled, this method will return null.
536: *
537: * @param task the ClusterTask object to be invoked on a given cluster member.
538: * @param nodeID the byte array that identifies the target cluster member.
539: * @return result of remote operation or null if operation failed or operation returned null.
540: * @throws IllegalStateException if requested node was not found or not running in a cluster.
541: */
542: public static Object doSynchronousClusterTask(ClusterTask task,
543: byte[] nodeID) {
544: return cacheFactoryStrategy.doSynchronousClusterTask(task,
545: nodeID);
546: }
547:
548: public static synchronized void initialize()
549: throws InitializationException {
550: try {
551: cacheFactoryStrategy = (CacheFactoryStrategy) Class
552: .forName(localCacheFactoryClass).newInstance();
553: } catch (InstantiationException e) {
554: throw new InitializationException(e);
555: } catch (IllegalAccessException e) {
556: throw new InitializationException(e);
557: } catch (ClassNotFoundException e) {
558: throw new InitializationException(e);
559: }
560: }
561:
562: private static ClassLoader getClusteredCacheStrategyClassLoader(
563: String pluginName) {
564: PluginManager pluginManager = XMPPServer.getInstance()
565: .getPluginManager();
566: Plugin enterprisePlugin = pluginManager.getPlugin(pluginName);
567: PluginClassLoader pluginLoader = pluginManager
568: .getPluginClassloader(enterprisePlugin);
569: if (pluginLoader != null) {
570: return pluginLoader;
571: } else {
572: Log.debug("Unable to find PluginClassloader for plugin: "
573: + pluginName + " in CacheFactory.");
574: return Thread.currentThread().getContextClassLoader();
575: }
576: }
577:
578: public static void startClustering() {
579: clusteringStarted = false;
580: clusteringStarting = true;
581: try {
582: cacheFactoryStrategy = (CacheFactoryStrategy) Class
583: .forName(
584: clusteredCacheFactoryClass,
585: true,
586: getClusteredCacheStrategyClassLoader("enterprise"))
587: .newInstance();
588: clusteringStarted = cacheFactoryStrategy.startCluster();
589: } catch (Exception e) {
590: Log
591: .error(
592: "Unable to start clustering - continuing in local mode",
593: e);
594: }
595: if (!clusteringStarted) {
596: // Revert to local cache factory if cluster fails to start
597: try {
598: cacheFactoryStrategy = (CacheFactoryStrategy) Class
599: .forName(localCacheFactoryClass).newInstance();
600: } catch (Exception e) {
601: Log
602: .error(
603: "Fatal error - Failed to join the cluster and failed to use local cache",
604: e);
605: }
606: } else {
607: if (statsThread == null) {
608: // Start a timing thread with 1 second of accuracy.
609: statsThread = new Thread("Cache Stats") {
610: private volatile boolean destroyed = false;
611:
612: public void run() {
613: XMPPServer.getInstance().addServerListener(
614: new XMPPServerListener() {
615: public void serverStarted() {
616: }
617:
618: public void serverStopping() {
619: destroyed = true;
620: }
621: });
622: ClusterManager
623: .addListener(new ClusterEventListener() {
624: public void joinedCluster() {
625: }
626:
627: public void joinedCluster(
628: byte[] nodeID) {
629: }
630:
631: public void leftCluster() {
632: destroyed = true;
633: ClusterManager
634: .removeListener(this );
635: }
636:
637: public void leftCluster(
638: byte[] nodeID) {
639: }
640:
641: public void markedAsSeniorClusterMember() {
642: }
643: });
644:
645: // Run the timer indefinitely.
646: while (!destroyed
647: && ClusterManager.isClusteringEnabled()) {
648: // Publish cache stats for this cluster node (assuming clustering is
649: // enabled and there are stats to publish).
650: try {
651: cacheFactoryStrategy
652: .updateCacheStats(caches);
653: } catch (Exception e) {
654: Log.error(e);
655: }
656: try {
657: // Sleep 10 seconds.
658: sleep(10000);
659: } catch (InterruptedException ie) {
660: // Ignore.
661: }
662: }
663: statsThread = null;
664: Log.debug("Cache stats thread terminated.");
665: }
666: };
667: statsThread.setDaemon(true);
668: statsThread.start();
669: }
670: }
671: clusteringStarting = false;
672: }
673:
674: public static void stopClustering() {
675: try {
676: // Stop the cluster
677: cacheFactoryStrategy.stopCluster();
678: // Set the strategy to local
679: cacheFactoryStrategy = (CacheFactoryStrategy) Class
680: .forName(localCacheFactoryClass).newInstance();
681:
682: clusteringStarted = false;
683: } catch (Exception e) {
684: Log
685: .error(
686: "Unable to stop clustering - continuing in clustered mode",
687: e);
688: }
689: }
690:
691: /**
692: * Notification message indicating that this JVM has joined a cluster.
693: */
694: public static void joinedCluster() {
695: // Loop through local caches and switch them to clustered cache (migrate content)
696: for (Cache cache : getAllCaches()) {
697: CacheWrapper cacheWrapper = ((CacheWrapper) cache);
698: Cache clusteredCache = cacheFactoryStrategy
699: .createCache(cacheWrapper.getName());
700: cacheWrapper.setWrappedCache(clusteredCache);
701: }
702: }
703:
704: /**
705: * Notification message indicating that this JVM has left the cluster.
706: */
707: public static void leftCluster() {
708: // Loop through clustered caches and change them to local caches (migrate content)
709: try {
710: cacheFactoryStrategy = (CacheFactoryStrategy) Class
711: .forName(localCacheFactoryClass).newInstance();
712:
713: for (Cache cache : getAllCaches()) {
714: CacheWrapper cacheWrapper = ((CacheWrapper) cache);
715: Cache standaloneCache = cacheFactoryStrategy
716: .createCache(cacheWrapper.getName());
717: cacheWrapper.setWrappedCache(standaloneCache);
718: }
719: } catch (Exception e) {
720: Log.error("Error reverting caches to local caches", e);
721: }
722: }
723: }
|