001package org.apache.commons.jcs3.auxiliary.lateral; 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.Collections; 024import java.util.Map; 025import java.util.Set; 026 027import org.apache.commons.jcs3.auxiliary.AbstractAuxiliaryCacheEventLogging; 028import org.apache.commons.jcs3.auxiliary.lateral.behavior.ILateralCacheAttributes; 029import org.apache.commons.jcs3.engine.CacheInfo; 030import org.apache.commons.jcs3.engine.CacheStatus; 031import org.apache.commons.jcs3.engine.ZombieCacheServiceNonLocal; 032import org.apache.commons.jcs3.engine.behavior.ICacheElement; 033import org.apache.commons.jcs3.engine.behavior.ICacheServiceNonLocal; 034import org.apache.commons.jcs3.engine.behavior.IZombie; 035import org.apache.commons.jcs3.engine.stats.Stats; 036import org.apache.commons.jcs3.engine.stats.behavior.IStats; 037import org.apache.commons.jcs3.log.Log; 038import org.apache.commons.jcs3.log.LogManager; 039 040/** 041 * Lateral distributor. Returns null on get by default. Net search not implemented. 042 */ 043public class LateralCache<K, V> 044 extends AbstractAuxiliaryCacheEventLogging<K, V> 045{ 046 /** The logger. */ 047 private static final Log log = LogManager.getLog( LateralCache.class ); 048 049 /** generalize this, use another interface */ 050 private final ILateralCacheAttributes lateralCacheAttributes; 051 052 /** The region name */ 053 final String cacheName; 054 055 /** either http, socket.udp, or socket.tcp can set in config */ 056 private ICacheServiceNonLocal<K, V> lateralCacheService; 057 058 /** Monitors the connection. */ 059 private LateralCacheMonitor monitor; 060 061 /** 062 * Constructor for the LateralCache object 063 * <p> 064 * @param cattr 065 * @param lateral 066 * @param monitor 067 */ 068 public LateralCache( final ILateralCacheAttributes cattr, final ICacheServiceNonLocal<K, V> lateral, final LateralCacheMonitor monitor ) 069 { 070 this.cacheName = cattr.getCacheName(); 071 this.lateralCacheAttributes = cattr; 072 this.lateralCacheService = lateral; 073 this.monitor = monitor; 074 } 075 076 /** 077 * Constructor for the LateralCache object 078 * <p> 079 * @param cattr 080 * 081 * @deprecated Causes NPE 082 */ 083 @Deprecated 084 public LateralCache( final ILateralCacheAttributes cattr ) 085 { 086 this(cattr, null, null); 087 } 088 089 /** 090 * Update lateral. 091 * <p> 092 * @param ce 093 * @throws IOException 094 */ 095 @Override 096 protected void processUpdate( final ICacheElement<K, V> ce ) 097 throws IOException 098 { 099 try 100 { 101 if (ce != null) 102 { 103 log.debug( "update: lateral = [{0}], CacheInfo.listenerId = {1}", 104 lateralCacheService, CacheInfo.listenerId ); 105 lateralCacheService.update( ce, CacheInfo.listenerId ); 106 } 107 } 108 catch ( final IOException ex ) 109 { 110 handleException( ex, "Failed to put [" + ce.getKey() + "] to " + ce.getCacheName() + "@" + lateralCacheAttributes ); 111 } 112 } 113 114 /** 115 * The performance costs are too great. It is not recommended that you enable lateral gets. 116 * <p> 117 * @param key 118 * @return ICacheElement<K, V> or null 119 * @throws IOException 120 */ 121 @Override 122 protected ICacheElement<K, V> processGet( final K key ) 123 throws IOException 124 { 125 ICacheElement<K, V> obj = null; 126 127 if ( !this.lateralCacheAttributes.getPutOnlyMode() ) 128 { 129 try 130 { 131 obj = lateralCacheService.get( cacheName, key ); 132 } 133 catch ( final Exception e ) 134 { 135 log.error( e ); 136 handleException( e, "Failed to get [" + key + "] from " + lateralCacheAttributes.getCacheName() + "@" + lateralCacheAttributes ); 137 } 138 } 139 140 return obj; 141 } 142 143 /** 144 * @param pattern 145 * @return A map of K key to ICacheElement<K, V> element, or an empty map if there is no 146 * data in cache for any of these keys 147 * @throws IOException 148 */ 149 @Override 150 protected Map<K, ICacheElement<K, V>> processGetMatching( final String pattern ) 151 throws IOException 152 { 153 Map<K, ICacheElement<K, V>> map = Collections.emptyMap(); 154 155 if ( !this.lateralCacheAttributes.getPutOnlyMode() ) 156 { 157 try 158 { 159 return lateralCacheService.getMatching( cacheName, pattern ); 160 } 161 catch ( final IOException e ) 162 { 163 log.error( e ); 164 handleException( e, "Failed to getMatching [" + pattern + "] from " + lateralCacheAttributes.getCacheName() + "@" + lateralCacheAttributes ); 165 } 166 } 167 168 return map; 169 } 170 171 /** 172 * Return the keys in this cache. 173 * <p> 174 * @see org.apache.commons.jcs3.auxiliary.AuxiliaryCache#getKeySet() 175 */ 176 @Override 177 public Set<K> getKeySet() throws IOException 178 { 179 try 180 { 181 return lateralCacheService.getKeySet( cacheName ); 182 } 183 catch ( final IOException ex ) 184 { 185 handleException( ex, "Failed to get key set from " + lateralCacheAttributes.getCacheName() + "@" 186 + lateralCacheAttributes ); 187 } 188 return Collections.emptySet(); 189 } 190 191 /** 192 * Synchronously remove from the remote cache; if failed, replace the remote handle with a 193 * zombie. 194 * <p> 195 * @param key 196 * @return false always 197 * @throws IOException 198 */ 199 @Override 200 protected boolean processRemove( final K key ) 201 throws IOException 202 { 203 log.debug( "removing key: {0}", key ); 204 205 try 206 { 207 lateralCacheService.remove( cacheName, key, CacheInfo.listenerId ); 208 } 209 catch ( final IOException ex ) 210 { 211 handleException( ex, "Failed to remove " + key + " from " + lateralCacheAttributes.getCacheName() + "@" + lateralCacheAttributes ); 212 } 213 return false; 214 } 215 216 /** 217 * Synchronously removeAll from the remote cache; if failed, replace the remote handle with a 218 * zombie. 219 * <p> 220 * @throws IOException 221 */ 222 @Override 223 protected void processRemoveAll() 224 throws IOException 225 { 226 try 227 { 228 lateralCacheService.removeAll( cacheName, CacheInfo.listenerId ); 229 } 230 catch ( final IOException ex ) 231 { 232 handleException( ex, "Failed to remove all from " + lateralCacheAttributes.getCacheName() + "@" + lateralCacheAttributes ); 233 } 234 } 235 236 /** 237 * Synchronously dispose the cache. Not sure we want this. 238 * <p> 239 * @throws IOException 240 */ 241 @Override 242 protected void processDispose() 243 throws IOException 244 { 245 log.debug( "Disposing of lateral cache" ); 246 247 try 248 { 249 lateralCacheService.dispose( this.lateralCacheAttributes.getCacheName() ); 250 // Should remove connection 251 } 252 catch ( final IOException ex ) 253 { 254 log.error( "Couldn't dispose", ex ); 255 handleException( ex, "Failed to dispose " + lateralCacheAttributes.getCacheName() ); 256 } 257 } 258 259 /** 260 * Returns the cache status. 261 * <p> 262 * @return The status value 263 */ 264 @Override 265 public CacheStatus getStatus() 266 { 267 return this.lateralCacheService instanceof IZombie ? CacheStatus.ERROR : CacheStatus.ALIVE; 268 } 269 270 /** 271 * Returns the current cache size. 272 * <p> 273 * @return The size value 274 */ 275 @Override 276 public int getSize() 277 { 278 return 0; 279 } 280 281 /** 282 * Gets the cacheType attribute of the LateralCache object 283 * <p> 284 * @return The cacheType value 285 */ 286 @Override 287 public CacheType getCacheType() 288 { 289 return CacheType.LATERAL_CACHE; 290 } 291 292 /** 293 * Gets the cacheName attribute of the LateralCache object 294 * <p> 295 * @return The cacheName value 296 */ 297 @Override 298 public String getCacheName() 299 { 300 return cacheName; 301 } 302 303 /** 304 * Not yet sure what to do here. 305 * <p> 306 * @param ex 307 * @param msg 308 * @throws IOException 309 */ 310 private void handleException( final Exception ex, final String msg ) 311 throws IOException 312 { 313 log.error( "Disabling lateral cache due to error {0}", msg, ex ); 314 315 lateralCacheService = new ZombieCacheServiceNonLocal<>( lateralCacheAttributes.getZombieQueueMaxSize() ); 316 // may want to flush if region specifies 317 // Notify the cache monitor about the error, and kick off the recovery 318 // process. 319 monitor.notifyError(); 320 321 // could stop the net search if it is built and try to reconnect? 322 if ( ex instanceof IOException ) 323 { 324 throw (IOException) ex; 325 } 326 throw new IOException( ex.getMessage() ); 327 } 328 329 /** 330 * Replaces the current remote cache service handle with the given handle. 331 * <p> 332 * @param restoredLateral 333 */ 334 public void fixCache( final ICacheServiceNonLocal<K, V> restoredLateral ) 335 { 336 if ( this.lateralCacheService instanceof ZombieCacheServiceNonLocal ) 337 { 338 final ZombieCacheServiceNonLocal<K, V> zombie = (ZombieCacheServiceNonLocal<K, V>) this.lateralCacheService; 339 this.lateralCacheService = restoredLateral; 340 try 341 { 342 zombie.propagateEvents( restoredLateral ); 343 } 344 catch ( final Exception e ) 345 { 346 try 347 { 348 handleException( e, "Problem propagating events from Zombie Queue to new Lateral Service." ); 349 } 350 catch ( final IOException e1 ) 351 { 352 // swallow, since this is just expected kick back. Handle always throws 353 } 354 } 355 } 356 else 357 { 358 this.lateralCacheService = restoredLateral; 359 } 360 } 361 362 /** 363 * getStats 364 * <p> 365 * @return String 366 */ 367 @Override 368 public String getStats() 369 { 370 return ""; 371 } 372 373 /** 374 * @return Returns the AuxiliaryCacheAttributes. 375 */ 376 @Override 377 public ILateralCacheAttributes getAuxiliaryCacheAttributes() 378 { 379 return lateralCacheAttributes; 380 } 381 382 /** 383 * @return debugging data. 384 */ 385 @Override 386 public String toString() 387 { 388 final StringBuilder buf = new StringBuilder(); 389 buf.append( "\n LateralCache " ); 390 buf.append( "\n Cache Name [" + lateralCacheAttributes.getCacheName() + "]" ); 391 buf.append( "\n cattr = [" + lateralCacheAttributes + "]" ); 392 return buf.toString(); 393 } 394 395 /** 396 * @return extra data. 397 */ 398 @Override 399 public String getEventLoggingExtraInfo() 400 { 401 return null; 402 } 403 404 /** 405 * The NoWait on top does not call out to here yet. 406 * <p> 407 * @return almost nothing 408 */ 409 @Override 410 public IStats getStatistics() 411 { 412 final IStats stats = new Stats(); 413 stats.setTypeName( "LateralCache" ); 414 return stats; 415 } 416}