1 /* 2 * Copyright (C) 2004-2008 Jive Software. All rights reserved. 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 package org.jivesoftware.util.cache; 17 18 import org.jivesoftware.openfire.XMPPServer; 19 import org.jivesoftware.openfire.XMPPServerListener; 20 import org.jivesoftware.openfire.cluster.ClusterEventListener; 21 import org.jivesoftware.openfire.cluster.ClusterManager; 22 import org.jivesoftware.openfire.cluster.ClusterNodeInfo; 23 import org.jivesoftware.openfire.cluster.ClusterPacketRouter; 24 import org.jivesoftware.openfire.container.Plugin; 25 import org.jivesoftware.openfire.container.PluginClassLoader; 26 import org.jivesoftware.openfire.container.PluginManager; 27 import org.jivesoftware.openfire.session.RemoteSessionLocatorImpl; 28 import org.jivesoftware.util.InitializationException; 29 import org.jivesoftware.util.JiveConstants; 30 import org.jivesoftware.util.JiveGlobals; 31 import org.jivesoftware.util.PropertyEventDispatcher; 32 import org.jivesoftware.util.PropertyEventListener; 33 import org.slf4j.Logger; 34 import org.slf4j.LoggerFactory; 35 import org.xmpp.packet.JID; 36 37 import java.net.URL; 38 import java.time.Duration; 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.Collection; 42 import java.util.Collections; 43 import java.util.HashMap; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.concurrent.ConcurrentHashMap; 47 import java.util.concurrent.TimeUnit; 48 import java.util.concurrent.locks.Lock; 49 50 /** 51 * Creates Cache objects. The returned caches will either be local or clustered 52 * depending on the clustering enabled setting and a user's license. 53 * 54 * <p>When clustered caching is turned on, cache usage statistics for all caches 55 * that have been created are periodically published to the clustered cache 56 * named "opt-$cacheStats".</p> 57 * 58 */ 59 @SuppressWarnings("rawtypes") 60 public class CacheFactory { 61 62 private static final Logger log = LoggerFactory.getLogger(CacheFactory.class); 63 64 public static String LOCAL_CACHE_PROPERTY_NAME = "cache.clustering.local.class"; 65 public static String CLUSTERED_CACHE_PROPERTY_NAME = "cache.clustering.clustered.class"; 66 67 private static boolean clusteringStarted = false; 68 private static boolean clusteringStarting = false; 69 70 /** 71 * Storage for all caches that get created. 72 */ 73 private static Map<String, Cache> caches = new ConcurrentHashMap<>(); 74 private static List<String> localOnly = Collections.synchronizedList(new ArrayList<String>()); 75 76 private static String localCacheFactoryClass; 77 private static String clusteredCacheFactoryClass; 78 private static CacheFactoryStrategy cacheFactoryStrategy = new DefaultLocalCacheStrategy(); 79 private static CacheFactoryStrategy localCacheFactoryStrategy; 80 private static CacheFactoryStrategy clusteredCacheFactoryStrategy; 81 private static Thread statsThread; 82 83 public static final int DEFAULT_MAX_CACHE_SIZE = 1024 * 256; 84 public static final long DEFAULT_MAX_CACHE_LIFETIME = 6 * JiveConstants.HOUR; 85 86 /** 87 * This map contains property names which were used to store cache configuration data 88 * in local xml properties in previous versions. 89 */ 90 private static final Map<String, String> cacheNames = new HashMap<>(); 91 /** 92 * Default properties to use for local caches. Default properties can be overridden 93 * by setting the corresponding system properties. 94 */ 95 private static final Map<String, Long> cacheProps = new HashMap<>(); 96 97 private static final String PROPERTY_PREFIX_CACHE = "cache."; 98 99 private static final String PROPERTY_SUFFIX_MAX_LIFE_TIME = ".maxLifetime"; 100 101 private static final String PROPERTY_SUFFIX_SIZE = ".size"; 102 103 private static final String PROPERTY_SUFFIX_TYPE = ".type"; 104 105 private static final String PROPERTY_SUFFIX_MIN = ".min"; 106 107 static { 108 localCacheFactoryClass = JiveGlobals.getProperty(LOCAL_CACHE_PROPERTY_NAME, 109 "org.jivesoftware.util.cache.DefaultLocalCacheStrategy"); 110 clusteredCacheFactoryClass = JiveGlobals.getProperty(CLUSTERED_CACHE_PROPERTY_NAME, 111 "org.jivesoftware.openfire.plugin.util.cache.ClusteredCacheFactory"); 112 113 cacheNames.put("DNS Records", "dnsRecords"); 114 cacheNames.put("Favicon Hits", "faviconHits"); 115 cacheNames.put("Favicon Misses", "faviconMisses"); 116 cacheNames.put("Group", "group"); 117 cacheNames.put("Group Metadata Cache", "groupMeta"); 118 cacheNames.put("Javascript Cache", "javascript"); 119 cacheNames.put("Last Activity Cache", "lastActivity"); 120 cacheNames.put("Multicast Service", "multicast"); 121 cacheNames.put("Offline Message Size", "offlinemessage"); 122 cacheNames.put("Offline Presence Cache", "offlinePresence"); 123 cacheNames.put("Privacy Lists", "listsCache"); 124 cacheNames.put("Remote Users Existence", "remoteUsersCache"); 125 cacheNames.put("Roster", "username2roster"); 126 cacheNames.put("RosterItems", "username2rosterItems"); 127 cacheNames.put("User", "userCache"); 128 cacheNames.put("Locked Out Accounts", "lockOutCache"); 129 cacheNames.put("VCard", "vcardCache"); 130 cacheNames.put("File Transfer Cache", "fileTransfer"); 131 cacheNames.put("File Transfer", "transferProxy"); 132 cacheNames.put("POP3 Authentication", "pop3"); 133 cacheNames.put("LDAP Authentication", "ldap"); 134 cacheNames.put("Routing Servers Cache", "routeServer"); 135 cacheNames.put("Routing Components Cache", "routeComponent"); 136 cacheNames.put("Routing Users Cache", "routeUser"); 137 cacheNames.put("Routing AnonymousUsers Cache", "routeAnonymousUser"); 138 cacheNames.put("Routing User Sessions", "routeUserSessions"); 139 cacheNames.put("Routing Result Listeners", "routeResultListeners"); 140 cacheNames.put("Components", "components"); 141 cacheNames.put("Components Sessions", "componentsSessions"); 142 cacheNames.put("Connection Managers Sessions", "connManagerSessions"); 143 cacheNames.put("Incoming Server Sessions", "incServerSessions"); 144 cacheNames.put("Sessions by Hostname", "sessionsHostname"); 145 cacheNames.put("Secret Keys Cache", "secretKeys"); 146 cacheNames.put("Validated Domains", "validatedDomains"); 147 cacheNames.put("Directed Presences", "directedPresences"); 148 cacheNames.put("Disco Server Features", "serverFeatures"); 149 cacheNames.put("Disco Server Items", "serverItems"); 150 cacheNames.put("Remote Server Configurations", "serversConfigurations"); 151 cacheNames.put("Entity Capabilities", "entityCapabilities"); 152 cacheNames.put("Entity Capabilities Users", "entityCapabilitiesUsers"); 153 cacheNames.put("PEPServiceManager", "pepServiceManager"); 154 cacheNames.put("Published Items", "publishedItems"); 155 cacheNames.put("JID Node-parts", "jidNodeprep"); 156 cacheNames.put("JID Domain-parts", "jidDomainprep"); 157 cacheNames.put("JID Resource-parts", "jidResourceprep"); 158 cacheNames.put("Sequences", "sequences"); 159 cacheNames.put("MUC Service Pings Sent", "mucPings"); 160 161 cacheProps.put(PROPERTY_PREFIX_CACHE + "dnsRecords" + PROPERTY_SUFFIX_SIZE, 128 * 1024L); 162 cacheProps.put(PROPERTY_PREFIX_CACHE + "dnsRecords" + PROPERTY_SUFFIX_MAX_LIFE_TIME, 1000 * 60L); 163 cacheProps.put(PROPERTY_PREFIX_CACHE + "fileTransfer" + PROPERTY_SUFFIX_SIZE, 128 * 1024L); 164 cacheProps.put(PROPERTY_PREFIX_CACHE + "fileTransfer" + PROPERTY_SUFFIX_MAX_LIFE_TIME, 1000 * 60 * 10L); 165 cacheProps.put(PROPERTY_PREFIX_CACHE + "multicast" + PROPERTY_SUFFIX_SIZE, 128 * 1024L); 166 cacheProps.put(PROPERTY_PREFIX_CACHE + "multicast" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.DAY); 167 cacheProps.put(PROPERTY_PREFIX_CACHE + "offlinemessage" + PROPERTY_SUFFIX_SIZE, 100 * 1024L); 168 cacheProps.put(PROPERTY_PREFIX_CACHE + "offlinemessage" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.HOUR * 12); 169 cacheProps.put(PROPERTY_PREFIX_CACHE + "pop3" + PROPERTY_SUFFIX_SIZE, 512 * 1024L); 170 cacheProps.put(PROPERTY_PREFIX_CACHE + "pop3" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.HOUR); 171 cacheProps.put(PROPERTY_PREFIX_CACHE + "transferProxy" + PROPERTY_SUFFIX_SIZE, -1L); 172 cacheProps.put(PROPERTY_PREFIX_CACHE + "transferProxy" + PROPERTY_SUFFIX_MAX_LIFE_TIME, 1000 * 60 * 10L); 173 cacheProps.put(PROPERTY_PREFIX_CACHE + "group" + PROPERTY_SUFFIX_SIZE, 1024 * 1024L); 174 cacheProps.put(PROPERTY_PREFIX_CACHE + "group" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.MINUTE * 15); 175 cacheProps.put(PROPERTY_PREFIX_CACHE + "lockOutCache" + PROPERTY_SUFFIX_SIZE, 1024 * 1024L); 176 cacheProps.put(PROPERTY_PREFIX_CACHE + "lockOutCache" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.MINUTE * 15); 177 cacheProps.put(PROPERTY_PREFIX_CACHE + "groupMeta" + PROPERTY_SUFFIX_SIZE, 512 * 1024L); 178 cacheProps.put(PROPERTY_PREFIX_CACHE + "groupMeta" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.MINUTE * 15); 179 cacheProps.put(PROPERTY_PREFIX_CACHE + "username2roster" + PROPERTY_SUFFIX_SIZE, 1024 * 1024L); 180 cacheProps.put(PROPERTY_PREFIX_CACHE + "username2roster" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.MINUTE * 30); 181 cacheProps.put(PROPERTY_PREFIX_CACHE + "username2rosterItems" + PROPERTY_SUFFIX_SIZE, 1024 * 1024L); 182 cacheProps.put(PROPERTY_PREFIX_CACHE + "username2rosterItems" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.MINUTE * 10); 183 cacheProps.put(PROPERTY_PREFIX_CACHE + "javascript" + PROPERTY_SUFFIX_SIZE, 128 * 1024L); 184 cacheProps.put(PROPERTY_PREFIX_CACHE + "javascript" + PROPERTY_SUFFIX_MAX_LIFE_TIME, 3600 * 24 * 10L); 185 cacheProps.put(PROPERTY_PREFIX_CACHE + "ldap" + PROPERTY_SUFFIX_SIZE, 512 * 1024L); 186 cacheProps.put(PROPERTY_PREFIX_CACHE + "ldap" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.HOUR * 2); 187 cacheProps.put(PROPERTY_PREFIX_CACHE + "listsCache" + PROPERTY_SUFFIX_SIZE, 512 * 1024L); 188 cacheProps.put(PROPERTY_PREFIX_CACHE + "offlinePresence" + PROPERTY_SUFFIX_SIZE, 512 * 1024L); 189 cacheProps.put(PROPERTY_PREFIX_CACHE + "lastActivity" + PROPERTY_SUFFIX_SIZE, 128 * 1024L); 190 cacheProps.put(PROPERTY_PREFIX_CACHE + "userCache" + PROPERTY_SUFFIX_SIZE, 512 * 1024L); 191 cacheProps.put(PROPERTY_PREFIX_CACHE + "userCache" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.MINUTE * 30); 192 cacheProps.put(PROPERTY_PREFIX_CACHE + "remoteUsersCache" + PROPERTY_SUFFIX_SIZE, 512 * 1024L); 193 cacheProps.put(PROPERTY_PREFIX_CACHE + "remoteUsersCache" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.MINUTE * 30); 194 cacheProps.put(PROPERTY_PREFIX_CACHE + "vcardCache" + PROPERTY_SUFFIX_SIZE, 512 * 1024L); 195 cacheProps.put(PROPERTY_PREFIX_CACHE + "faviconHits" + PROPERTY_SUFFIX_SIZE, 128 * 1024L); 196 cacheProps.put(PROPERTY_PREFIX_CACHE + "faviconMisses" + PROPERTY_SUFFIX_SIZE, 128 * 1024L); 197 cacheProps.put(PROPERTY_PREFIX_CACHE + "routeServer" + PROPERTY_SUFFIX_SIZE, -1L); 198 cacheProps.put(PROPERTY_PREFIX_CACHE + "routeServer" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 199 cacheProps.put(PROPERTY_PREFIX_CACHE + "routeComponent" + PROPERTY_SUFFIX_SIZE, -1L); 200 cacheProps.put(PROPERTY_PREFIX_CACHE + "routeComponent" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 201 cacheProps.put(PROPERTY_PREFIX_CACHE + "routeUser" + PROPERTY_SUFFIX_SIZE, -1L); 202 cacheProps.put(PROPERTY_PREFIX_CACHE + "routeUser" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 203 cacheProps.put(PROPERTY_PREFIX_CACHE + "routeAnonymousUser" + PROPERTY_SUFFIX_SIZE, -1L); 204 cacheProps.put(PROPERTY_PREFIX_CACHE + "routeAnonymousUser" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 205 cacheProps.put(PROPERTY_PREFIX_CACHE + "routeUserSessions" + PROPERTY_SUFFIX_SIZE, -1L); 206 cacheProps.put(PROPERTY_PREFIX_CACHE + "routeUserSessions" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 207 cacheProps.put(PROPERTY_PREFIX_CACHE + "routeResultListeners" + PROPERTY_SUFFIX_SIZE, -1L); 208 cacheProps.put(PROPERTY_PREFIX_CACHE + "routeResultListeners" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 209 cacheProps.put(PROPERTY_PREFIX_CACHE + "components" + PROPERTY_SUFFIX_SIZE, -1L); 210 cacheProps.put(PROPERTY_PREFIX_CACHE + "components" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 211 cacheProps.put(PROPERTY_PREFIX_CACHE + "componentsSessions" + PROPERTY_SUFFIX_SIZE, -1L); 212 cacheProps.put(PROPERTY_PREFIX_CACHE + "componentsSessions" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 213 cacheProps.put(PROPERTY_PREFIX_CACHE + "connManagerSessions" + PROPERTY_SUFFIX_SIZE, -1L); 214 cacheProps.put(PROPERTY_PREFIX_CACHE + "connManagerSessions" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 215 cacheProps.put(PROPERTY_PREFIX_CACHE + "incServerSessions" + PROPERTY_SUFFIX_SIZE, -1L); 216 cacheProps.put(PROPERTY_PREFIX_CACHE + "incServerSessions" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 217 cacheProps.put(PROPERTY_PREFIX_CACHE + "sessionsHostname" + PROPERTY_SUFFIX_SIZE, -1L); 218 cacheProps.put(PROPERTY_PREFIX_CACHE + "sessionsHostname" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 219 cacheProps.put(PROPERTY_PREFIX_CACHE + "secretKeys" + PROPERTY_SUFFIX_SIZE, -1L); 220 cacheProps.put(PROPERTY_PREFIX_CACHE + "secretKeys" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 221 cacheProps.put(PROPERTY_PREFIX_CACHE + "validatedDomains" + PROPERTY_SUFFIX_SIZE, -1L); 222 cacheProps.put(PROPERTY_PREFIX_CACHE + "validatedDomains" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 223 cacheProps.put(PROPERTY_PREFIX_CACHE + "directedPresences" + PROPERTY_SUFFIX_SIZE, -1L); 224 cacheProps.put(PROPERTY_PREFIX_CACHE + "directedPresences" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 225 cacheProps.put(PROPERTY_PREFIX_CACHE + "serverFeatures" + PROPERTY_SUFFIX_SIZE, -1L); 226 cacheProps.put(PROPERTY_PREFIX_CACHE + "serverFeatures" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 227 cacheProps.put(PROPERTY_PREFIX_CACHE + "serverItems" + PROPERTY_SUFFIX_SIZE, -1L); 228 cacheProps.put(PROPERTY_PREFIX_CACHE + "serverItems" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 229 cacheProps.put(PROPERTY_PREFIX_CACHE + "serversConfigurations" + PROPERTY_SUFFIX_SIZE, 128 * 1024L); 230 cacheProps.put(PROPERTY_PREFIX_CACHE + "serversConfigurations" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.MINUTE * 30); 231 cacheProps.put(PROPERTY_PREFIX_CACHE + "entityCapabilities" + PROPERTY_SUFFIX_SIZE, -1L); 232 cacheProps.put(PROPERTY_PREFIX_CACHE + "entityCapabilities" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.DAY * 2); 233 cacheProps.put(PROPERTY_PREFIX_CACHE + "entityCapabilitiesUsers" + PROPERTY_SUFFIX_SIZE, -1L); 234 cacheProps.put(PROPERTY_PREFIX_CACHE + "entityCapabilitiesUsers" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.DAY * 2); 235 cacheProps.put(PROPERTY_PREFIX_CACHE + "pluginCacheInfo" + PROPERTY_SUFFIX_SIZE, -1L); 236 cacheProps.put(PROPERTY_PREFIX_CACHE + "pluginCacheInfo" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 237 cacheProps.put(PROPERTY_PREFIX_CACHE + "pepServiceManager" + PROPERTY_SUFFIX_SIZE, 1024L * 1024 * 10); 238 cacheProps.put(PROPERTY_PREFIX_CACHE + "pepServiceManager" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.MINUTE * 30); 239 cacheProps.put(PROPERTY_PREFIX_CACHE + "publishedItems" + PROPERTY_SUFFIX_SIZE, 1024L * 1024 * 10); 240 cacheProps.put(PROPERTY_PREFIX_CACHE + "publishedItems" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.MINUTE * 15); 241 cacheProps.put(PROPERTY_PREFIX_CACHE + "sequences" + PROPERTY_SUFFIX_SIZE, -1L); 242 cacheProps.put(PROPERTY_PREFIX_CACHE + "sequences" + PROPERTY_SUFFIX_MAX_LIFE_TIME, -1L); 243 cacheProps.put(PROPERTY_PREFIX_CACHE + "mucPings" + PROPERTY_SUFFIX_SIZE, -1L); 244 cacheProps.put(PROPERTY_PREFIX_CACHE + "mucPings" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JiveConstants.MINUTE * 30); 245 246 // The JID-based classes (wrappers for Caffeine caches) take their default values from whatever is hardcoded in the JID implementation. 247 cacheProps.put(PROPERTY_PREFIX_CACHE + "jidNodeprep" + PROPERTY_SUFFIX_SIZE, JID.NODEPREP_CACHE.policy().eviction().get().getMaximum() ); 248 cacheProps.put(PROPERTY_PREFIX_CACHE + "jidNodeprep" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JID.NODEPREP_CACHE.policy().expireAfterWrite().get().getExpiresAfter( TimeUnit.MILLISECONDS ) ); 249 cacheProps.put(PROPERTY_PREFIX_CACHE + "jidDomainprep" + PROPERTY_SUFFIX_SIZE, JID.DOMAINPREP_CACHE.policy().eviction().get().getMaximum() ); 250 cacheProps.put(PROPERTY_PREFIX_CACHE + "jidDomainprep" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JID.DOMAINPREP_CACHE.policy().expireAfterWrite().get().getExpiresAfter( TimeUnit.MILLISECONDS ) ); 251 cacheProps.put(PROPERTY_PREFIX_CACHE + "jidResourceprep" + PROPERTY_SUFFIX_SIZE, JID.RESOURCEPREP_CACHE.policy().eviction().get().getMaximum() ); 252 cacheProps.put(PROPERTY_PREFIX_CACHE + "jidResourceprep" + PROPERTY_SUFFIX_MAX_LIFE_TIME, JID.RESOURCEPREP_CACHE.policy().expireAfterWrite().get().getExpiresAfter( TimeUnit.MILLISECONDS ) ); 253 PropertyEventDispatcher.addListener( new PropertyEventListener() { @Override public void propertySet( String property, Map<String, Object> params ) { final Cache cache = getCacheByProperty( property ); if ( cache == null ) { return; } if (property.endsWith(PROPERTY_SUFFIX_SIZE)) { final long size = getMaxCacheSize( cache.getName() ); cache.setMaxCacheSize( size ); } if (property.endsWith(PROPERTY_SUFFIX_MAX_LIFE_TIME)) { final long lifetime = getMaxCacheLifetime( cache.getName() ); cache.setMaxLifetime( lifetime ); } } @Override public void propertyDeleted( String property, Map<String, Object> params ) { propertySet( property, params ); } @Override public void xmlPropertySet( String property, Map<String, Object> params ) { propertySet( property, params ); } @Override public void xmlPropertyDeleted( String property, Map<String, Object> params ) { propertySet( property, params ); } } )254 PropertyEventDispatcher.addListener( new PropertyEventListener() 255 { 256 257 @Override 258 public void propertySet( String property, Map<String, Object> params ) 259 { 260 final Cache cache = getCacheByProperty( property ); 261 if ( cache == null ) 262 { 263 return; 264 } 265 266 if (property.endsWith(PROPERTY_SUFFIX_SIZE)) 267 { 268 final long size = getMaxCacheSize( cache.getName() ); 269 cache.setMaxCacheSize( size ); 270 } 271 272 if (property.endsWith(PROPERTY_SUFFIX_MAX_LIFE_TIME)) 273 { 274 final long lifetime = getMaxCacheLifetime( cache.getName() ); 275 cache.setMaxLifetime( lifetime ); 276 } 277 278 // Note that changes to 'min' and 'type' cannot be applied runtime - a restart is required for those. 279 } 280 281 @Override 282 public void propertyDeleted( String property, Map<String, Object> params ) 283 { 284 propertySet( property, params ); 285 } 286 287 @Override 288 public void xmlPropertySet( String property, Map<String, Object> params ) 289 { 290 propertySet( property, params ); 291 } 292 293 @Override 294 public void xmlPropertyDeleted( String property, Map<String, Object> params ) 295 { 296 propertySet( property, params ); 297 } 298 } ); 299 } 300 301 // this field is unused, but retains a reference that intends to prevent the monitor from being garbage collected. 302 @SuppressWarnings("unused") 303 private static ConsistencyMonitor consistencyMonitor; 304 CacheFactory()305 private CacheFactory() { 306 } 307 308 /** 309 * If a local property is found for the supplied name which specifies a value for cache size, it is returned. 310 * Otherwise, the defaultSize argument is returned. 311 * 312 * @param cacheName the name of the cache to look up a corresponding property for. 313 * @return either the property value or the default value. 314 */ getMaxCacheSize(String cacheName)315 public static long getMaxCacheSize(String cacheName) { 316 return getCacheProperty(cacheName, PROPERTY_SUFFIX_SIZE, DEFAULT_MAX_CACHE_SIZE); 317 } 318 319 /** 320 * Sets a local property which overrides the maximum cache size for the 321 * supplied cache name. 322 * @param cacheName the name of the cache to store a value for. 323 * @param size the maximum cache size. 324 */ setMaxSizeProperty(String cacheName, long size)325 public static void setMaxSizeProperty(String cacheName, long size) { 326 cacheName = cacheName.replaceAll(" ", ""); 327 if ( !Long.toString(size).equals(JiveGlobals.getProperty(PROPERTY_PREFIX_CACHE + cacheName + PROPERTY_SUFFIX_SIZE))) 328 { 329 JiveGlobals.setProperty(PROPERTY_PREFIX_CACHE + cacheName + PROPERTY_SUFFIX_SIZE, Long.toString(size)); 330 } 331 } 332 hasMaxSizeFromProperty(String cacheName)333 public static boolean hasMaxSizeFromProperty(String cacheName) { 334 return hasCacheProperty(cacheName, PROPERTY_SUFFIX_SIZE); 335 } 336 337 /** 338 * If a local property is found for the supplied name which specifies a value for cache entry lifetime, it 339 * is returned. Otherwise, the defaultLifetime argument is returned. 340 * 341 * @param cacheName the name of the cache to look up a corresponding property for. 342 * @return either the property value or the default value. 343 */ getMaxCacheLifetime(String cacheName)344 public static long getMaxCacheLifetime(String cacheName) { 345 return getCacheProperty(cacheName, PROPERTY_SUFFIX_MAX_LIFE_TIME, DEFAULT_MAX_CACHE_LIFETIME); 346 } 347 348 /** 349 * Sets a local property which overrides the maximum cache entry lifetime 350 * for the supplied cache name. 351 * @param cacheName the name of the cache to store a value for. 352 * @param lifetime the maximum cache entry lifetime. 353 */ setMaxLifetimeProperty(String cacheName, long lifetime)354 public static void setMaxLifetimeProperty(String cacheName, long lifetime) { 355 cacheName = cacheName.replaceAll(" ", ""); 356 if ( !Long.toString(lifetime).equals(JiveGlobals.getProperty(PROPERTY_PREFIX_CACHE + cacheName + PROPERTY_SUFFIX_MAX_LIFE_TIME))) 357 { 358 JiveGlobals.setProperty((PROPERTY_PREFIX_CACHE + cacheName + PROPERTY_SUFFIX_MAX_LIFE_TIME), Long.toString(lifetime)); 359 } 360 } 361 hasMaxLifetimeFromProperty(String cacheName)362 public static boolean hasMaxLifetimeFromProperty(String cacheName) { 363 return hasCacheProperty(cacheName, PROPERTY_SUFFIX_MAX_LIFE_TIME); 364 } 365 setCacheTypeProperty(String cacheName, String type)366 public static void setCacheTypeProperty(String cacheName, String type) { 367 cacheName = cacheName.replaceAll(" ", ""); 368 if ( !type.equals(JiveGlobals.getProperty(PROPERTY_PREFIX_CACHE + cacheName + PROPERTY_SUFFIX_TYPE))) 369 { 370 JiveGlobals.setProperty(PROPERTY_PREFIX_CACHE + cacheName + PROPERTY_SUFFIX_TYPE, type); 371 } 372 } 373 getCacheTypeProperty(String cacheName)374 public static String getCacheTypeProperty(String cacheName) { 375 cacheName = cacheName.replaceAll(" ", ""); 376 return JiveGlobals.getProperty(PROPERTY_PREFIX_CACHE + cacheName + PROPERTY_SUFFIX_TYPE); 377 } 378 setMinCacheSize(String cacheName, long size)379 public static void setMinCacheSize(String cacheName, long size) { 380 cacheName = cacheName.replaceAll(" ", ""); 381 if ( !Long.toString(size).equals(JiveGlobals.getProperty(PROPERTY_PREFIX_CACHE + cacheName + PROPERTY_SUFFIX_MIN))) 382 { 383 JiveGlobals.setProperty(PROPERTY_PREFIX_CACHE + cacheName + PROPERTY_SUFFIX_MIN, Long.toString(size)); 384 } 385 } 386 getMinCacheSize(String cacheName)387 public static long getMinCacheSize(String cacheName) { 388 return getCacheProperty(cacheName, PROPERTY_SUFFIX_MIN, 0); 389 } 390 getCacheByProperty( String property )391 private static Cache getCacheByProperty( String property ) 392 { 393 if ( !property.startsWith(PROPERTY_PREFIX_CACHE)) 394 { 395 return null; 396 } 397 398 // Extract the cache name identifier from the property name. 399 final String name = property.substring(PROPERTY_PREFIX_CACHE.length(), property.lastIndexOf(".")); 400 401 // See if property is using the short name variant. 402 for ( final Map.Entry<String, String> entry : cacheNames.entrySet() ) 403 { 404 if ( name.equals( entry.getValue() ) ) 405 { 406 return caches.get( entry.getKey() ); 407 } 408 } 409 410 // If not a short name, then try for a normalized name. 411 for ( final Map.Entry<String, Cache> entry : caches.entrySet() ) 412 { 413 if ( entry.getKey().replaceAll(" ", "").equals( name ) ) 414 { 415 return entry.getValue(); 416 } 417 } 418 419 return null; 420 } 421 getCacheProperty(String cacheName, String suffix, long defaultValue)422 private static long getCacheProperty(String cacheName, String suffix, long defaultValue) { 423 // First check if user is overwriting default value using a system property for the cache name 424 String propName = PROPERTY_PREFIX_CACHE + cacheName.replaceAll(" ", "") + suffix; 425 String sizeProp = JiveGlobals.getProperty(propName); 426 if (sizeProp == null && cacheNames.containsKey(cacheName)) { 427 // No system property was found for the cache name so try now with short name 428 propName = PROPERTY_PREFIX_CACHE + cacheNames.get(cacheName) + suffix; 429 sizeProp = JiveGlobals.getProperty(propName); 430 } 431 if (sizeProp != null) { 432 try { 433 return Long.parseLong(sizeProp); 434 } 435 catch (NumberFormatException nfe) { 436 log.warn("Unable to parse " + propName + " using default value."); 437 } 438 } 439 // Check if there is a default size value for this cache 440 Long defaultSize = cacheProps.get(propName); 441 return defaultSize == null ? defaultValue : defaultSize; 442 } 443 hasCacheProperty(String cacheName, String suffix)444 private static boolean hasCacheProperty(String cacheName, String suffix) { 445 // First check if user is overwriting default value using a system property for the cache name 446 String propName = PROPERTY_PREFIX_CACHE + cacheName.replaceAll(" ", "") + suffix; 447 String sizeProp = JiveGlobals.getProperty(propName); 448 if (sizeProp == null && cacheNames.containsKey(cacheName)) { 449 // No system property was found for the cache name so try now with short name 450 propName = PROPERTY_PREFIX_CACHE + cacheNames.get(cacheName) + suffix; 451 sizeProp = JiveGlobals.getProperty(propName); 452 } 453 if (sizeProp != null) { 454 try { 455 Long.parseLong(sizeProp); 456 return true; 457 } 458 catch (NumberFormatException nfe) { 459 log.warn("Unable to parse " + propName + " using default value."); 460 } 461 } 462 return false; 463 } 464 465 /** 466 * Returns an array of all caches in the system. 467 * @return an array of all caches in the system. 468 */ getAllCaches()469 public static Cache[] getAllCaches() { 470 List<Cache> values = new ArrayList<>(); 471 for (Cache cache : caches.values()) { 472 values.add(cache); 473 } 474 return values.toArray(new Cache[values.size()]); 475 } 476 477 /** 478 * Returns the named cache, creating it as necessary. 479 * 480 * @param name the name of the cache to create. 481 * @param <T> the type cache being created 482 * @return the named cache, creating it as necessary. 483 */ 484 @SuppressWarnings("unchecked") createCache(String name)485 public static synchronized <T extends Cache> T createCache(String name) { 486 T cache = (T) caches.get(name); 487 if (cache != null) { 488 return cache; 489 } 490 cache = (T) cacheFactoryStrategy.createCache(name); 491 492 log.info("Created cache [" + cacheFactoryStrategy.getClass().getName() + "] for " + name); 493 494 return wrapCache(cache, name); 495 } 496 497 /** 498 * Returns the serializing cache, creating it as necessary. Unlike the caches returned by 499 * {@link #createCache(String)}, the caches returned by this method store data in serialized form (without a 500 * reference to their class). 501 * 502 * The primary benefit of usage of this cache is that the cached data is stored without any references to their 503 * classes. This allows cache content to remain usable after the classes that instantiate the data get reloaded. 504 * This is of particular interest when the cache is used to store data provided by Openfire plugins (as these 505 * classes get loaded by a class loader that is replaced when a plugin gets reloaded or upgraded). 506 * 507 * As compared to other caches, usage of this cache will require more system resources, as the serialized 508 * representation of an object typically is (much) larger than its original (unserialized) form. 509 * 510 * @param name The name of the cache to create. 511 * @param keyClass The class of instances used as keys. 512 * @param valueClass The class of instances used as values. 513 * @return the named cache, creating it as necessary. 514 * @see <a href="https://igniterealtime.atlassian.net/browse/OF-2239">Issue OF-2239: Make it easier to cache plugin class instances</a> 515 */ 516 @SuppressWarnings("unchecked") createSerializingCache(String name, Class keyClass, Class valueClass)517 public static synchronized SerializingCache createSerializingCache(String name, Class keyClass, Class valueClass) { 518 SerializingCache cache = (SerializingCache) caches.get(name); 519 if (cache != null) { 520 return cache; 521 } 522 523 final Cache<String, String> delegate = (Cache<String, String>) cacheFactoryStrategy.createCache(name); 524 cache = new SerializingCache(delegate, keyClass, valueClass); 525 526 log.info("Created serializing cache [" + cacheFactoryStrategy.getClass().getName() + "] for " + name); 527 528 return wrapCache(cache, name); 529 } 530 531 /** 532 * Returns the named local cache, creating it as necessary. 533 * 534 * @param name the name of the cache to create. 535 * @param <T> the type cache being created 536 * @return the named cache, creating it as necessary. 537 */ 538 @SuppressWarnings("unchecked") createLocalCache(String name)539 public static synchronized <T extends Cache> T createLocalCache(String name) { 540 T cache = (T) caches.get(name); 541 if (cache != null) { 542 return cache; 543 } 544 cache = (T) localCacheFactoryStrategy.createCache(name); 545 localOnly.add(name); 546 547 log.info("Created local-only cache [" + localCacheFactoryClass + "] for " + name); 548 549 return wrapCache(cache, name); 550 } 551 552 /** 553 * Destroys the cache for the cache name specified. 554 * 555 * @param name the name of the cache to destroy. 556 */ destroyCache(String name)557 public static synchronized void destroyCache(String name) { 558 Cache cache = caches.remove(name); 559 if (cache != null) { 560 if (localOnly.contains(name)) { 561 localOnly.remove(name); 562 localCacheFactoryStrategy.destroyCache(cache); 563 } else { 564 cacheFactoryStrategy.destroyCache(cache); 565 } 566 } 567 } 568 569 /** 570 * @deprecated in favour of {@link Cache#getLock}. Will be removed in Openfire 5.0.0. 571 * 572 * <p>Returns an existing {@link java.util.concurrent.locks.Lock} on the specified key or creates a new one 573 * if none was found. This operation is thread safe. Successive calls with the same key may or may not 574 * return the same {@link java.util.concurrent.locks.Lock}. However, different threads asking for the 575 * same Lock at the same time will get the same Lock object.<p> 576 * 577 * The supplied cache may or may not be used depending whether the server is running on cluster mode 578 * or not. When not running as part of a cluster then the lock will be unrelated to the cache and will 579 * only be visible in this JVM. 580 * 581 * @param key the object that defines the visibility or scope of the lock. 582 * @param cache the cache used for holding the lock. 583 * @return an existing lock on the specified key or creates a new one if none was found. 584 */ 585 @Deprecated getLock(Object key, Cache cache)586 public static synchronized Lock getLock(Object key, Cache cache) { 587 if (localOnly.contains(cache.getName())) { 588 return localCacheFactoryStrategy.getLock(key, cache); 589 } else { 590 return cacheFactoryStrategy.getLock(key, cache); 591 } 592 } 593 594 @SuppressWarnings("unchecked") wrapCache(T cache, String name)595 private static <T extends Cache> T wrapCache(T cache, String name) { 596 if ("Routing Components Cache".equals(name)) { 597 cache = (T) new ComponentCacheWrapper(cache); 598 } else { 599 cache = (T) new CacheWrapper(cache); 600 } 601 cache.setName(name); 602 603 caches.put(name, cache); 604 return cache; 605 } 606 607 /** 608 * Returns true if clustering is installed and can be used by this JVM 609 * to join a cluster. A false value could mean that either clustering 610 * support is not available or the license does not allow to have more 611 * than 1 cluster node. 612 * 613 * @return true if clustering is installed and can be used by 614 * this JVM to join a cluster. 615 */ isClusteringAvailable()616 public static boolean isClusteringAvailable() { 617 if (clusteredCacheFactoryStrategy == null) { 618 try { 619 clusteredCacheFactoryStrategy = (CacheFactoryStrategy) Class.forName( 620 clusteredCacheFactoryClass, true, 621 getClusteredCacheStrategyClassLoader()).newInstance(); 622 } catch (NoClassDefFoundError | Exception e) { 623 log.warn("Clustered cache factory strategy " + clusteredCacheFactoryClass + " not found"); 624 } 625 } 626 return (clusteredCacheFactoryStrategy != null); 627 } 628 629 /** 630 * Returns true is clustering is currently being started. Once the cluster 631 * is started or failed to be started this value will be false. 632 * 633 * @return true is clustering is currently being started. 634 */ isClusteringStarting()635 public static boolean isClusteringStarting() { 636 return clusteringStarting; 637 } 638 639 /** 640 * Returns true if this node is currently a member of a cluster. The last step of application 641 * initialization is to join a cluster, so this method returns false during most of application startup. 642 * 643 * @return true if this node is currently a member of a cluster. 644 */ isClusteringStarted()645 public static boolean isClusteringStarted() { 646 return clusteringStarted; 647 } 648 649 /** 650 * Returns a byte[] that uniquely identifies this member within the cluster or {@code null} 651 * when not in a cluster. 652 * 653 * @return a byte[] that uniquely identifies this member within the cluster or null when not in a cluster. 654 */ getClusterMemberID()655 public static byte[] getClusterMemberID() { 656 return cacheFactoryStrategy.getClusterMemberID(); 657 } 658 clearCaches()659 public synchronized static void clearCaches() { 660 for (String cacheName : caches.keySet()) { 661 Cache cache = caches.get(cacheName); 662 cache.clear(); 663 } 664 } 665 clearCaches( String... cacheName )666 public synchronized static void clearCaches( String... cacheName ) 667 { 668 caches.values().parallelStream() 669 .filter(cache -> Arrays.asList(cacheName).contains(cache.getName())) 670 .forEach(Map::clear); 671 } 672 673 /** 674 * Returns a byte[] that uniquely identifies this senior cluster member or {@code null} 675 * when not in a cluster. 676 * 677 * @return a byte[] that uniquely identifies this senior cluster member or null when not in a cluster. 678 */ getSeniorClusterMemberID()679 public static byte[] getSeniorClusterMemberID() { 680 return cacheFactoryStrategy.getSeniorClusterMemberID(); 681 } 682 683 /** 684 * Returns true if this member is the senior member in the cluster. If clustering 685 * is not enabled, this method will also return true. This test is useful for 686 * tasks that should only be run on a single member in a cluster. 687 * 688 * @return true if this cluster member is the senior or if clustering is not enabled. 689 */ isSeniorClusterMember()690 public static boolean isSeniorClusterMember() { 691 return cacheFactoryStrategy.isSeniorClusterMember(); 692 } 693 694 /** 695 * Returns basic information about the current members of the cluster or an empty 696 * collection if not running in a cluster. 697 * 698 * @return information about the current members of the cluster or an empty 699 * collection if not running in a cluster. 700 */ getClusterNodesInfo()701 public static Collection<ClusterNodeInfo> getClusterNodesInfo() { 702 return cacheFactoryStrategy.getClusterNodesInfo(); 703 } 704 705 /** 706 * Returns the maximum number of cluster members allowed. A value of 0 will 707 * be returned when clustering is not allowed. 708 * 709 * @return the maximum number of cluster members allowed or 0 if clustering is not allowed. 710 */ getMaxClusterNodes()711 public static int getMaxClusterNodes() { 712 return cacheFactoryStrategy.getMaxClusterNodes(); 713 } 714 715 /** 716 * Gets the pseudo-synchronized time from the cluster. While the cluster members may 717 * have varying system times, this method is expected to return a timestamp that is 718 * synchronized (or nearly so; best effort) across the cluster. 719 * 720 * @return Synchronized time for all cluster members 721 */ getClusterTime()722 public static long getClusterTime() { 723 // use try/catch here for backward compatibility with older plugin(s) 724 try { return cacheFactoryStrategy.getClusterTime(); } 725 catch (AbstractMethodError ame) { 726 log.warn("Cluster time not available; check for update to hazelcast/clustering plugin"); 727 return localCacheFactoryStrategy.getClusterTime(); 728 } 729 } 730 731 /** 732 * Invokes a task on other cluster members in an asynchronous fashion. The task will not be 733 * executed on the local cluster member. If clustering is not enabled, this method 734 * will do nothing. 735 * 736 * @param task the task to be invoked on all other cluster members. 737 */ doClusterTask(final ClusterTask<?> task)738 public static void doClusterTask(final ClusterTask<?> task) { 739 cacheFactoryStrategy.doClusterTask(task); 740 } 741 742 /** 743 * Invokes a task on a given cluster member in an asynchronous fashion. If clustering is not enabled, 744 * this method will do nothing. 745 * 746 * @param task the task to be invoked on the specified cluster member. 747 * @param nodeID the byte array that identifies the target cluster member. 748 * @throws IllegalStateException if requested node was not found or not running in a cluster. 749 */ doClusterTask(final ClusterTask<?> task, byte[] nodeID)750 public static void doClusterTask(final ClusterTask<?> task, byte[] nodeID) { 751 cacheFactoryStrategy.doClusterTask(task, nodeID); 752 } 753 754 /** 755 * Invokes a task on other cluster members synchronously and returns the result as a Collection 756 * (method will not return until the task has been executed on each cluster member). 757 * The task will not be executed on the local cluster member. If clustering is not enabled, 758 * this method will return an empty collection. 759 * 760 * @param task the ClusterTask object to be invoked on all other cluster members. 761 * @param includeLocalMember true to run the task on the local member, false otherwise 762 * @param <T> the return type of the cluster task 763 * @return collection with the result of the execution. 764 */ doSynchronousClusterTask(ClusterTask<T> task, boolean includeLocalMember)765 public static <T> Collection<T> doSynchronousClusterTask(ClusterTask<T> task, boolean includeLocalMember) { 766 return cacheFactoryStrategy.doSynchronousClusterTask(task, includeLocalMember); 767 } 768 769 /** 770 * Invokes a task on a given cluster member synchronously and returns the result of 771 * the remote operation. If clustering is not enabled, this method will return null. 772 * 773 * @param task the ClusterTask object to be invoked on a given cluster member. 774 * @param nodeID the byte array that identifies the target cluster member. 775 * @param <T> the return type of the cluster task 776 * @return result of remote operation or null if operation failed or operation returned null. 777 * @throws IllegalStateException if requested node was not found or not running in a cluster. 778 */ doSynchronousClusterTask(ClusterTask<T> task, byte[] nodeID)779 public static <T> T doSynchronousClusterTask(ClusterTask<T> task, byte[] nodeID) { 780 return cacheFactoryStrategy.doSynchronousClusterTask(task, nodeID); 781 } 782 783 /** 784 * Returns the node info for the given cluster node 785 * @param nodeID The target cluster node 786 * @return The info for the cluster node or null if not found 787 */ getClusterNodeInfo(byte[] nodeID)788 public static ClusterNodeInfo getClusterNodeInfo(byte[] nodeID) { 789 return cacheFactoryStrategy.getClusterNodeInfo(nodeID); 790 } 791 getPluginName()792 public static String getPluginName() { 793 return cacheFactoryStrategy.getPluginName(); 794 } 795 initialize()796 public static synchronized void initialize() throws InitializationException { 797 try { 798 localCacheFactoryStrategy = (CacheFactoryStrategy) Class.forName(localCacheFactoryClass).newInstance(); 799 cacheFactoryStrategy = localCacheFactoryStrategy; 800 801 // Update the JID-internal caches, if they're configured differently than their default. 802 JID.NODEPREP_CACHE.policy().eviction().get().setMaximum( getMaxCacheSize( "jidNodeprep" ) ); 803 JID.NODEPREP_CACHE.policy().expireAfterWrite().get().setExpiresAfter( getMaxCacheLifetime( "jidNodeprep" ), TimeUnit.MILLISECONDS ); 804 JID.DOMAINPREP_CACHE.policy().eviction().get().setMaximum( getMaxCacheSize( "jidDomainprep" ) ); 805 JID.DOMAINPREP_CACHE.policy().expireAfterWrite().get().setExpiresAfter( getMaxCacheLifetime( "jidDomainprep" ), TimeUnit.MILLISECONDS ); 806 JID.RESOURCEPREP_CACHE.policy().eviction().get().setMaximum( getMaxCacheSize( "jidResourceprep" ) ); 807 JID.RESOURCEPREP_CACHE.policy().expireAfterWrite().get().setExpiresAfter( getMaxCacheLifetime( "jidResourceprep" ), TimeUnit.MILLISECONDS ); 808 809 // Mock cache creation for the JID-internal classes, by wrapping them in a compatibility layer. 810 caches.put("JID Node-parts", CaffeineCache.of( JID.NODEPREP_CACHE, "JID Node-parts" )); 811 caches.put("JID Domain-parts", CaffeineCache.of( JID.DOMAINPREP_CACHE, "JID Domain-parts" )); 812 caches.put("JID Resource-parts", CaffeineCache.of( JID.RESOURCEPREP_CACHE, "JID Resource-parts" )); 813 814 } catch (Exception e) { 815 log.error("Failed to instantiate local cache factory strategy: " + localCacheFactoryClass, e); 816 throw new InitializationException(e); 817 } 818 819 consistencyMonitor = ConsistencyMonitor.getInstance(); 820 } 821 getClusteredCacheStrategyClassLoader()822 private static ClassLoader getClusteredCacheStrategyClassLoader() { 823 PluginManager pluginManager = XMPPServer.getInstance().getPluginManager(); 824 Plugin plugin = pluginManager.getPlugin("hazelcast"); 825 if (plugin == null) { 826 plugin = pluginManager.getPlugin("clustering"); 827 if (plugin == null) { 828 plugin = pluginManager.getPlugin("enterprise"); 829 } 830 } 831 PluginClassLoader pluginLoader = pluginManager.getPluginClassloader(plugin); 832 if (pluginLoader != null) { 833 if (log.isDebugEnabled()) { 834 StringBuffer pluginLoaderDetails = new StringBuffer("Clustering plugin class loader: "); 835 pluginLoaderDetails.append(pluginLoader.getClass().getName()); 836 for (URL url : pluginLoader.getURLs()) { 837 pluginLoaderDetails.append("\n\t").append(url.toExternalForm()); 838 } 839 log.debug(pluginLoaderDetails.toString()); 840 } 841 return pluginLoader; 842 } 843 else { 844 log.warn("CacheFactory - Unable to find a Plugin that provides clustering support."); 845 return Thread.currentThread().getContextClassLoader(); 846 } 847 } 848 startClustering()849 public static void startClustering() { 850 if (isClusteringAvailable()) { 851 clusteringStarting = true; 852 // Set session locator to use when in a cluster 853 XMPPServer.getInstance().setRemoteSessionLocator(new RemoteSessionLocatorImpl()); 854 // Set packet router to use to deliver packets to remote cluster nodes 855 XMPPServer.getInstance().getRoutingTable().setRemotePacketRouter(new ClusterPacketRouter()); 856 clusteringStarted = clusteredCacheFactoryStrategy.startCluster(); 857 clusteringStarting = false; 858 } 859 if (clusteringStarted) { 860 if (statsThread == null) { 861 // Start a timing thread with 1 second of accuracy. 862 statsThread = new Thread("Cache Stats") { 863 private volatile boolean destroyed = false; 864 865 @Override 866 public void run() { 867 XMPPServer.getInstance().addServerListener(new XMPPServerListener() { 868 @Override 869 public void serverStarted() {} 870 871 @Override 872 public void serverStopping() { 873 destroyed = true; 874 } 875 }); 876 ClusterManager.addListener(new ClusterEventListener() { 877 @Override 878 public void joinedCluster() {} 879 880 @Override 881 public void joinedCluster(byte[] nodeID) {} 882 883 @Override 884 public void leftCluster() { 885 destroyed = true; 886 ClusterManager.removeListener(this); 887 log.debug("CacheFactory is not listening for cluster events anymore because it left the cluster"); 888 } 889 890 @Override 891 public void leftCluster(byte[] nodeID) {} 892 893 @Override 894 public void markedAsSeniorClusterMember() {} 895 }); 896 897 // Run the timer indefinitely. 898 while (!destroyed && ClusterManager.isClusteringEnabled()) { 899 // Publish cache stats for this cluster node (assuming clustering is 900 // enabled and there are stats to publish). 901 try { 902 cacheFactoryStrategy.updateCacheStats(caches); 903 } 904 catch (Exception e) { 905 log.error(e.getMessage(), e); 906 } 907 try { 908 // Sleep 10 seconds. 909 sleep(10000); 910 } 911 catch (InterruptedException ie) { 912 // Ignore. 913 } 914 } 915 statsThread = null; 916 log.debug("Cache stats thread terminated."); 917 } 918 }; 919 statsThread.setDaemon(true); 920 statsThread.start(); 921 } 922 } 923 } 924 stopClustering()925 public static void stopClustering() { 926 // Stop the cluster 927 clusteredCacheFactoryStrategy.stopCluster(); 928 clusteredCacheFactoryStrategy = null; 929 XMPPServer.getInstance().setRemoteSessionLocator(null); 930 XMPPServer.getInstance().getRoutingTable().setRemotePacketRouter(null); 931 // Set the strategy to local 932 cacheFactoryStrategy = localCacheFactoryStrategy; 933 } 934 935 /** 936 * Notification message indicating that this JVM has joined a cluster. 937 */ 938 @SuppressWarnings("unchecked") joinedCluster()939 public static synchronized void joinedCluster() { 940 cacheFactoryStrategy = clusteredCacheFactoryStrategy; 941 // Loop through local caches and switch them to clustered cache (purge content) 942 Arrays.stream(getAllCaches()) 943 .filter(CacheFactory::isClusterableCache) 944 .forEach(cache -> { 945 final CacheWrapper cacheWrapper = ((CacheWrapper) cache); 946 final Cache clusteredCache = cacheFactoryStrategy.createCache(cacheWrapper.getName()); 947 cacheWrapper.setWrappedCache(clusteredCache); 948 }); 949 clusteringStarting = false; 950 clusteringStarted = true; 951 log.info("Clustering started; cache migration complete"); 952 } 953 954 /** 955 * Notification message indicating that this JVM has left the cluster. 956 */ 957 @SuppressWarnings("unchecked") leftCluster()958 public static synchronized void leftCluster() { 959 clusteringStarted = false; 960 cacheFactoryStrategy = localCacheFactoryStrategy; 961 962 // Loop through clustered caches and change them to local caches (purge content) 963 Arrays.stream(getAllCaches()) 964 .filter(CacheFactory::isClusterableCache) 965 .forEach(cache -> { 966 final CacheWrapper cacheWrapper = ((CacheWrapper) cache); 967 final Cache standaloneCache = cacheFactoryStrategy.createCache(cacheWrapper.getName()); 968 cacheWrapper.setWrappedCache(standaloneCache); 969 }); 970 log.info("Clustering stopped; cache migration complete"); 971 } 972 973 /** 974 * Indicates if the supplied Cache is "clusterable". This is used to determine if a cache should be migrated 975 * between a {@link DefaultCache} and a clustered cache when the node joins/leaves the cluster. 976 * <p> 977 * A cache is considered 'clusterable' if; 978 * <ul> 979 * <li>the cache is not a 'local' cache - which apply to the local node only so do not need to be clustered, and</li> 980 * <li>the cache is actually a {@link CacheWrapper} which wraps the underlying default or clustered cache</li> 981 * </ul> 982 * 983 * @param cache the cache to check 984 * @return {@code true} if the cache can be converted to/from a clustered cache, otherwise {@code false} 985 */ isClusterableCache(final Cache cache)986 private static boolean isClusterableCache(final Cache cache) { 987 return cache instanceof CacheWrapper && !localOnly.contains(cache.getName()); 988 } 989 990 } 991