001package org.apache.commons.jcs.auxiliary.remote; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.IOException; 023import java.util.ArrayList; 024import java.util.Collections; 025import java.util.HashSet; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029 030import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCache; 031import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes; 032import org.apache.commons.jcs.engine.CacheStatus; 033import org.apache.commons.jcs.engine.behavior.ICache; 034import org.apache.commons.jcs.engine.behavior.ICacheElement; 035import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager; 036import org.apache.commons.jcs.engine.behavior.IElementSerializer; 037import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger; 038import org.apache.commons.jcs.engine.stats.StatElement; 039import org.apache.commons.jcs.engine.stats.Stats; 040import org.apache.commons.jcs.engine.stats.behavior.IStatElement; 041import org.apache.commons.jcs.engine.stats.behavior.IStats; 042import org.apache.commons.logging.Log; 043import org.apache.commons.logging.LogFactory; 044 045/** An abstract base for the No Wait Facade. Different implementations will failover differently. */ 046public abstract class AbstractRemoteCacheNoWaitFacade<K, V> 047 extends AbstractAuxiliaryCache<K, V> 048{ 049 /** log instance */ 050 private static final Log log = LogFactory.getLog( AbstractRemoteCacheNoWaitFacade.class ); 051 052 /** The connection to a remote server, or a zombie. */ 053 protected List<RemoteCacheNoWait<K, V>> noWaits; 054 055 /** holds failover and cluster information */ 056 private IRemoteCacheAttributes remoteCacheAttributes; 057 058 /** 059 * Constructs with the given remote cache, and fires events to any listeners. 060 * <p> 061 * @param noWaits 062 * @param rca 063 * @param cacheEventLogger 064 * @param elementSerializer 065 */ 066 public AbstractRemoteCacheNoWaitFacade( List<RemoteCacheNoWait<K,V>> noWaits, IRemoteCacheAttributes rca, 067 ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer ) 068 { 069 if ( log.isDebugEnabled() ) 070 { 071 log.debug( "CONSTRUCTING NO WAIT FACADE" ); 072 } 073 this.remoteCacheAttributes = rca; 074 setCacheEventLogger( cacheEventLogger ); 075 setElementSerializer( elementSerializer ); 076 this.noWaits = new ArrayList<RemoteCacheNoWait<K,V>>(noWaits); 077 for (RemoteCacheNoWait<K,V> nw : this.noWaits) 078 { 079 // FIXME: This cast is very brave. Remove this. 080 ((RemoteCache<K, V>)nw.getRemoteCache()).setFacade(this); 081 } 082 } 083 084 /** 085 * Constructs with the given remote cache, and fires events to any listeners. 086 * <p> 087 * @param noWaits 088 * @param rca 089 * @param cacheMgr 090 * @param cacheEventLogger 091 * @param elementSerializer 092 * @deprecated Unused parameter cacheMgr scheduled for removal 093 */ 094 @Deprecated 095 public AbstractRemoteCacheNoWaitFacade( List<ICache<K, V>> noWaits, RemoteCacheAttributes rca, 096 ICompositeCacheManager cacheMgr, ICacheEventLogger cacheEventLogger, 097 IElementSerializer elementSerializer ) 098 { 099 if ( log.isDebugEnabled() ) 100 { 101 log.debug( "CONSTRUCTING NO WAIT FACADE" ); 102 } 103 this.remoteCacheAttributes = rca; 104 setCacheEventLogger( cacheEventLogger ); 105 setElementSerializer( elementSerializer ); 106 this.noWaits = new ArrayList<RemoteCacheNoWait<K,V>>(); 107 for (ICache<K, V> nw : noWaits) 108 { 109 RemoteCacheNoWait<K,V> rcnw = (RemoteCacheNoWait<K,V>)nw; 110 ((RemoteCache<K, V>)rcnw.getRemoteCache()).setFacade(this); 111 this.noWaits.add(rcnw); 112 } 113 } 114 115 /** 116 * Put an element in the cache. 117 * <p> 118 * @param ce 119 * @throws IOException 120 */ 121 @Override 122 public void update( ICacheElement<K, V> ce ) 123 throws IOException 124 { 125 if ( log.isDebugEnabled() ) 126 { 127 log.debug( "updating through cache facade, noWaits.length = " + noWaits.size() ); 128 } 129 130 for (RemoteCacheNoWait<K, V> nw : noWaits) 131 { 132 try 133 { 134 nw.update( ce ); 135 // an initial move into a zombie will lock this to primary 136 // recovery. will not discover other servers until primary 137 // reconnect 138 // and subsequent error 139 } 140 catch ( IOException ex ) 141 { 142 String message = "Problem updating no wait. Will initiate failover if the noWait is in error."; 143 log.error( message, ex ); 144 145 if ( getCacheEventLogger() != null ) 146 { 147 getCacheEventLogger().logError( "RemoteCacheNoWaitFacade", 148 ICacheEventLogger.UPDATE_EVENT, 149 message + ":" + ex.getMessage() + " REGION: " + ce.getCacheName() 150 + " ELEMENT: " + ce ); 151 } 152 153 // can handle failover here? Is it safe to try the others? 154 // check to see it the noWait is now a zombie 155 // if it is a zombie, then move to the next in the failover list 156 // will need to keep them in order or a count 157 failover( nw ); 158 // should start a failover thread 159 // should probably only failover if there is only one in the noWait 160 // list 161 // Should start a background thread to restore the original primary if we are in failover state. 162 } 163 } 164 } 165 166 /** 167 * Synchronously reads from the remote cache. 168 * <p> 169 * @param key 170 * @return Either an ICacheElement<K, V> or null if it is not found. 171 */ 172 @Override 173 public ICacheElement<K, V> get( K key ) 174 { 175 for (RemoteCacheNoWait<K, V> nw : noWaits) 176 { 177 try 178 { 179 ICacheElement<K, V> obj = nw.get( key ); 180 if ( obj != null ) 181 { 182 return obj; 183 } 184 } 185 catch ( IOException ex ) 186 { 187 log.debug( "Failed to get." ); 188 return null; 189 } 190 } 191 return null; 192 } 193 194 /** 195 * Synchronously read from the remote cache. 196 * <p> 197 * @param pattern 198 * @return map 199 * @throws IOException 200 */ 201 @Override 202 public Map<K, ICacheElement<K, V>> getMatching( String pattern ) 203 throws IOException 204 { 205 for (RemoteCacheNoWait<K, V> nw : noWaits) 206 { 207 try 208 { 209 return nw.getMatching( pattern ); 210 } 211 catch ( IOException ex ) 212 { 213 log.debug( "Failed to getMatching." ); 214 } 215 } 216 return Collections.emptyMap(); 217 } 218 219 /** 220 * Gets multiple items from the cache based on the given set of keys. 221 * <p> 222 * @param keys 223 * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no 224 * data in cache for any of these keys 225 */ 226 @Override 227 public Map<K, ICacheElement<K, V>> getMultiple( Set<K> keys ) 228 { 229 if ( keys != null && !keys.isEmpty() ) 230 { 231 for (RemoteCacheNoWait<K, V> nw : noWaits) 232 { 233 try 234 { 235 return nw.getMultiple( keys ); 236 } 237 catch ( IOException ex ) 238 { 239 log.debug( "Failed to get." ); 240 } 241 } 242 } 243 244 return Collections.emptyMap(); 245 } 246 247 /** 248 * Return the keys in this cache. 249 * <p> 250 * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet() 251 */ 252 @Override 253 public Set<K> getKeySet() throws IOException 254 { 255 HashSet<K> allKeys = new HashSet<K>(); 256 for (RemoteCacheNoWait<K, V> nw : noWaits) 257 { 258 if ( nw != null ) 259 { 260 Set<K> keys = nw.getKeySet(); 261 if(keys != null) 262 { 263 allKeys.addAll( keys ); 264 } 265 } 266 } 267 return allKeys; 268 } 269 270 /** 271 * Adds a remove request to the remote cache. 272 * <p> 273 * @param key 274 * @return whether or not it was removed, right now it return false. 275 */ 276 @Override 277 public boolean remove( K key ) 278 { 279 try 280 { 281 for (RemoteCacheNoWait<K, V> nw : noWaits) 282 { 283 nw.remove( key ); 284 } 285 } 286 catch ( IOException ex ) 287 { 288 log.error( ex ); 289 } 290 return false; 291 } 292 293 /** 294 * Adds a removeAll request to the remote cache. 295 */ 296 @Override 297 public void removeAll() 298 { 299 try 300 { 301 for (RemoteCacheNoWait<K, V> nw : noWaits) 302 { 303 nw.removeAll(); 304 } 305 } 306 catch ( IOException ex ) 307 { 308 log.error( ex ); 309 } 310 } 311 312 /** Adds a dispose request to the remote cache. */ 313 @Override 314 public void dispose() 315 { 316 for (RemoteCacheNoWait<K, V> nw : noWaits) 317 { 318 nw.dispose(); 319 } 320 } 321 322 /** 323 * No remote invocation. 324 * <p> 325 * @return The size value 326 */ 327 @Override 328 public int getSize() 329 { 330 return 0; 331 // cache.getSize(); 332 } 333 334 /** 335 * Gets the cacheType attribute of the RemoteCacheNoWaitFacade object. 336 * <p> 337 * @return The cacheType value 338 */ 339 @Override 340 public CacheType getCacheType() 341 { 342 return CacheType.REMOTE_CACHE; 343 } 344 345 /** 346 * Gets the cacheName attribute of the RemoteCacheNoWaitFacade object. 347 * <p> 348 * @return The cacheName value 349 */ 350 @Override 351 public String getCacheName() 352 { 353 return remoteCacheAttributes.getCacheName(); 354 } 355 356 /** 357 * Gets the status attribute of the RemoteCacheNoWaitFacade object 358 * <p> 359 * Return ALIVE if any are alive. 360 * <p> 361 * @return The status value 362 */ 363 @Override 364 public CacheStatus getStatus() 365 { 366 for (RemoteCacheNoWait<K, V> nw : noWaits) 367 { 368 if ( nw.getStatus() == CacheStatus.ALIVE ) 369 { 370 return CacheStatus.ALIVE; 371 } 372 } 373 374 return CacheStatus.DISPOSED; 375 } 376 377 /** 378 * String form of some of the configuration information for the remote cache. 379 * <p> 380 * @return Some info for logging. 381 */ 382 @Override 383 public String toString() 384 { 385 return "RemoteCacheNoWaitFacade: " + remoteCacheAttributes.getCacheName() + ", rca = " + remoteCacheAttributes; 386 } 387 388 /** 389 * Begin the failover process if this is a local cache. Clustered remote caches do not failover. 390 * <p> 391 * @param rcnw The no wait in error. 392 */ 393 protected abstract void failover( RemoteCacheNoWait<K, V> rcnw ); 394 395 /** 396 * Get the primary server from the list of failovers 397 * 398 * @return a no wait 399 */ 400 public RemoteCacheNoWait<K, V> getPrimaryServer() 401 { 402 return noWaits.get(0); 403 } 404 405 /** 406 * restore the primary server in the list of failovers 407 * 408 */ 409 public void restorePrimaryServer(RemoteCacheNoWait<K, V> rcnw) 410 { 411 noWaits.clear(); 412 noWaits.add(rcnw); 413 } 414 415 /** 416 * @return Returns the AuxiliaryCacheAttributes. 417 */ 418 @Override 419 public IRemoteCacheAttributes getAuxiliaryCacheAttributes() 420 { 421 return this.remoteCacheAttributes; 422 } 423 424 /** 425 * getStats 426 * @return String 427 */ 428 @Override 429 public String getStats() 430 { 431 return getStatistics().toString(); 432 } 433 434 /** 435 * @return statistics about the cache region 436 */ 437 @Override 438 public IStats getStatistics() 439 { 440 IStats stats = new Stats(); 441 stats.setTypeName( "Remote Cache No Wait Facade" ); 442 443 ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>(); 444 445 if ( noWaits != null ) 446 { 447 elems.add(new StatElement<Integer>( "Number of No Waits", Integer.valueOf(noWaits.size()) ) ); 448 449 for ( RemoteCacheNoWait<K, V> rcnw : noWaits ) 450 { 451 // get the stats from the super too 452 IStats sStats = rcnw.getStatistics(); 453 elems.addAll(sStats.getStatElements()); 454 } 455 } 456 457 stats.setStatElements( elems ); 458 459 return stats; 460 } 461 462 /** 463 * This typically returns end point info . 464 * <p> 465 * @return the name 466 */ 467 @Override 468 public String getEventLoggingExtraInfo() 469 { 470 return "Remote Cache No Wait Facade"; 471 } 472}