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.ArrayList; 024import java.util.Arrays; 025import java.util.HashMap; 026import java.util.HashSet; 027import java.util.List; 028import java.util.Map; 029import java.util.Map.Entry; 030import java.util.Objects; 031import java.util.Set; 032import java.util.concurrent.ConcurrentHashMap; 033import java.util.concurrent.atomic.AtomicBoolean; 034import java.util.stream.Collectors; 035 036import org.apache.commons.jcs3.auxiliary.AbstractAuxiliaryCache; 037import org.apache.commons.jcs3.auxiliary.lateral.behavior.ILateralCacheAttributes; 038import org.apache.commons.jcs3.auxiliary.lateral.behavior.ILateralCacheListener; 039import org.apache.commons.jcs3.engine.CacheStatus; 040import org.apache.commons.jcs3.engine.behavior.ICacheElement; 041import org.apache.commons.jcs3.engine.stats.StatElement; 042import org.apache.commons.jcs3.engine.stats.Stats; 043import org.apache.commons.jcs3.engine.stats.behavior.IStatElement; 044import org.apache.commons.jcs3.engine.stats.behavior.IStats; 045import org.apache.commons.jcs3.log.Log; 046import org.apache.commons.jcs3.log.LogManager; 047 048/** 049 * Used to provide access to multiple services under nowait protection. Composite factory should 050 * construct LateralCacheNoWaitFacade to give to the composite cache out of caches it constructs 051 * from the varies manager to lateral services. Perhaps the lateralcache factory should be able to 052 * do this. 053 */ 054public class LateralCacheNoWaitFacade<K, V> 055 extends AbstractAuxiliaryCache<K, V> 056{ 057 /** The logger */ 058 private static final Log log = LogManager.getLog( LateralCacheNoWaitFacade.class ); 059 060 /** 061 * The queuing facade to the client. 062 * @deprecated Should not have been public in the first place 063 */ 064 @Deprecated 065 public LateralCacheNoWait<K, V>[] noWaits; 066 067 /** 068 * The queuing facade to the client. 069 */ 070 private final ConcurrentHashMap<String, LateralCacheNoWait<K, V>> noWaitMap; 071 072 /** The region name */ 073 private final String cacheName; 074 075 /** A cache listener */ 076 private ILateralCacheListener<K, V> listener; 077 078 /** User configurable attributes. */ 079 private final ILateralCacheAttributes lateralCacheAttributes; 080 081 /** Disposed state of this facade */ 082 private final AtomicBoolean disposed = new AtomicBoolean(false); 083 084 /** 085 * Constructs with the given lateral cache, and fires events to any listeners. 086 * <p> 087 * @param listener the cache listener 088 * @param noWaits the array of noWaits 089 * @param cattr the configuration 090 * 091 * @deprecated Use list constructor 092 */ 093 @Deprecated 094 public LateralCacheNoWaitFacade(final ILateralCacheListener<K, V> listener, final LateralCacheNoWait<K, V>[] noWaits, final ILateralCacheAttributes cattr ) 095 { 096 this(listener, Arrays.asList(noWaits), cattr); 097 } 098 099 /** 100 * Constructs with the given lateral cache, and fires events to any listeners. 101 * <p> 102 * @param listener the cache listener 103 * @param noWaits the list of noWaits 104 * @param cattr the configuration 105 * @since 3.1 106 */ 107 @SuppressWarnings("unchecked") // No generic arrays in java 108 public LateralCacheNoWaitFacade(final ILateralCacheListener<K, V> listener, 109 final List<LateralCacheNoWait<K, V>> noWaits, final ILateralCacheAttributes cattr ) 110 { 111 log.debug( "CONSTRUCTING NO WAIT FACADE" ); 112 this.listener = listener; 113 this.noWaits = noWaits.toArray(new LateralCacheNoWait[0]); 114 this.noWaitMap = new ConcurrentHashMap<>(); 115 noWaits.forEach(noWait -> noWaitMap.put(noWait.getIdentityKey(), noWait)); 116 this.cacheName = cattr.getCacheName(); 117 this.lateralCacheAttributes = cattr; 118 } 119 120 /** 121 * Return the size of the no wait list (for testing) 122 * 123 * @return the noWait list size. 124 * @since 3.1 125 */ 126 protected int getNoWaitSize() 127 { 128 return noWaitMap.size(); 129 } 130 131 /** 132 * Tells you if the no wait is in the list or not. 133 * <p> 134 * @param noWait 135 * @return true if the noWait is in the list. 136 */ 137 public boolean containsNoWait( final LateralCacheNoWait<K, V> noWait ) 138 { 139 return containsNoWait(noWait.getIdentityKey()); 140 } 141 142 /** 143 * Tells you if the no wait is in the list or not by checking for its 144 * identifying key 145 * <p> 146 * @param tcpServer the identifying key 147 * @return true if the noWait is in the list. 148 * @since 3.1 149 */ 150 public boolean containsNoWait(final String tcpServer) 151 { 152 return noWaitMap.containsKey(tcpServer); 153 } 154 155 /** 156 * Adds a no wait to the list if it isn't already in the list. 157 * <p> 158 * @param noWait 159 * @return true if it wasn't already contained 160 */ 161 @SuppressWarnings("unchecked") // No generic arrays in Java 162 public synchronized boolean addNoWait( final LateralCacheNoWait<K, V> noWait ) 163 { 164 if ( noWait == null ) 165 { 166 return false; 167 } 168 169 final LateralCacheNoWait<K,V> added = 170 noWaitMap.putIfAbsent(noWait.getIdentityKey(), noWait); 171 172 if (added != null) 173 { 174 log.debug( "No Wait already contained, [{0}]", noWait ); 175 return false; 176 } 177 178 noWaits = noWaitMap.values().toArray(new LateralCacheNoWait[0]); 179 180 return true; 181 } 182 183 /** 184 * Removes a no wait from the list if it is already there. 185 * <p> 186 * @param noWait 187 * @return true if it was already in the array 188 */ 189 public synchronized boolean removeNoWait( final LateralCacheNoWait<K, V> noWait ) 190 { 191 if (noWait == null) 192 { 193 return false; 194 } 195 196 return removeNoWait(noWait.getIdentityKey()); 197 } 198 199 /** 200 * Removes a no wait from the list if it is already there by its 201 * identifying key 202 * <p> 203 * @param tcpServer the identifying key. 204 * @return true if it was already in the array 205 * @since 3.1 206 */ 207 @SuppressWarnings("unchecked") // No generic arrays in java 208 public synchronized boolean removeNoWait(final String tcpServer) 209 { 210 if (tcpServer == null) 211 { 212 return false; 213 } 214 215 final LateralCacheNoWait<K,V> contained = noWaitMap.remove(tcpServer); 216 noWaits = noWaitMap.values().toArray(new LateralCacheNoWait[0]); 217 218 if (contained != null) 219 { 220 contained.dispose(); 221 } 222 223 return contained != null; 224 } 225 226 /** 227 * Update the cache element in all lateral caches 228 * @param ce the cache element 229 * @throws IOException 230 */ 231 @Override 232 public void update( final ICacheElement<K, V> ce ) 233 throws IOException 234 { 235 log.debug("updating through lateral cache facade, noWaits.length = {0}", 236 noWaitMap::size); 237 238 for (final LateralCacheNoWait<K, V> nw : noWaitMap.values()) 239 { 240 nw.update( ce ); 241 } 242 } 243 244 /** 245 * Synchronously reads from the lateral cache. 246 * <p> 247 * @param key 248 * @return ICacheElement 249 */ 250 @Override 251 public ICacheElement<K, V> get( final K key ) 252 { 253 return noWaitMap.values().stream() 254 .map(nw -> nw.get(key)) 255 .filter(Objects::nonNull) 256 .findFirst() 257 .orElse(null); 258 } 259 260 /** 261 * Gets multiple items from the cache based on the given set of keys. 262 * <p> 263 * @param keys 264 * @return a map of K key to ICacheElement<K, V> element, or an empty map if there is no 265 * data in cache for any of these keys 266 */ 267 @Override 268 public Map<K, ICacheElement<K, V>> getMultiple(final Set<K> keys) 269 { 270 if (keys != null && !keys.isEmpty()) 271 { 272 return keys.stream() 273 .collect(Collectors.toMap( 274 key -> key, 275 this::get)).entrySet().stream() 276 .filter(entry -> entry.getValue() != null) 277 .collect(Collectors.toMap( 278 Entry::getKey, 279 Entry::getValue)); 280 } 281 282 return new HashMap<>(); 283 } 284 285 /** 286 * Synchronously reads from the lateral cache. Get a response from each! This will be slow. 287 * Merge them. 288 * <p> 289 * @param pattern 290 * @return ICacheElement 291 */ 292 @Override 293 public Map<K, ICacheElement<K, V>> getMatching(final String pattern) 294 { 295 return noWaitMap.values().stream() 296 .flatMap(nw -> nw.getMatching(pattern).entrySet().stream()) 297 .collect(Collectors.toMap( 298 Entry::getKey, 299 Entry::getValue)); 300 } 301 302 /** 303 * Return the keys in this cache. 304 * <p> 305 * @see org.apache.commons.jcs3.auxiliary.AuxiliaryCache#getKeySet() 306 */ 307 @Override 308 public Set<K> getKeySet() throws IOException 309 { 310 final HashSet<K> allKeys = new HashSet<>(); 311 for (final LateralCacheNoWait<K, V> nw : noWaitMap.values()) 312 { 313 final Set<K> keys = nw.getKeySet(); 314 if (keys != null) 315 { 316 allKeys.addAll(keys); 317 } 318 } 319 return allKeys; 320 } 321 322 /** 323 * Adds a remove request to the lateral cache. 324 * <p> 325 * @param key 326 * @return always false. 327 */ 328 @Override 329 public boolean remove( final K key ) 330 { 331 noWaitMap.values().forEach(nw -> nw.remove( key )); 332 return false; 333 } 334 335 /** 336 * Adds a removeAll request to the lateral cache. 337 */ 338 @Override 339 public void removeAll() 340 { 341 noWaitMap.values().forEach(LateralCacheNoWait::removeAll); 342 } 343 344 /** Adds a dispose request to the lateral cache. */ 345 @Override 346 public void dispose() 347 { 348 if (disposed.compareAndSet(false, true)) 349 { 350 if ( listener != null ) 351 { 352 listener.dispose(); 353 listener = null; 354 } 355 356 noWaitMap.values().forEach(LateralCacheNoWait::dispose); 357 noWaitMap.clear(); 358 } 359 } 360 361 /** 362 * No lateral invocation. 363 * @return The size value 364 */ 365 @Override 366 public int getSize() 367 { 368 return 0; 369 //cache.getSize(); 370 } 371 372 /** 373 * Gets the cacheType attribute of the LateralCacheNoWaitFacade object. 374 * <p> 375 * @return The cacheType value 376 */ 377 @Override 378 public CacheType getCacheType() 379 { 380 return CacheType.LATERAL_CACHE; 381 } 382 383 /** 384 * Gets the cacheName attribute of the LateralCacheNoWaitFacade object. 385 * <p> 386 * @return The cacheName value 387 */ 388 @Override 389 public String getCacheName() 390 { 391 return cacheName; 392 } 393 394 /** 395 * Gets the status attribute of the LateralCacheNoWaitFacade object 396 * @return The status value 397 */ 398 @Override 399 public CacheStatus getStatus() 400 { 401 if (disposed.get()) 402 { 403 return CacheStatus.DISPOSED; 404 } 405 406 if (noWaitMap.isEmpty() || listener != null) 407 { 408 return CacheStatus.ALIVE; 409 } 410 411 final List<CacheStatus> statii = noWaitMap.values().stream() 412 .map(LateralCacheNoWait::getStatus) 413 .collect(Collectors.toList()); 414 415 // It's alive if ANY of its nowaits is alive 416 if (statii.contains(CacheStatus.ALIVE)) 417 { 418 return CacheStatus.ALIVE; 419 } 420 // It's alive if ANY of its nowaits is in error, but 421 // none are alive, then it's in error 422 if (statii.contains(CacheStatus.ERROR)) 423 { 424 return CacheStatus.ERROR; 425 } 426 427 // Otherwise, it's been disposed, since it's the only status left 428 return CacheStatus.DISPOSED; 429 } 430 431 /** 432 * @return Returns the AuxiliaryCacheAttributes. 433 */ 434 @Override 435 public ILateralCacheAttributes getAuxiliaryCacheAttributes() 436 { 437 return this.lateralCacheAttributes; 438 } 439 440 /** 441 * @return "LateralCacheNoWaitFacade: " + cacheName; 442 */ 443 @Override 444 public String toString() 445 { 446 return "LateralCacheNoWaitFacade: " + cacheName; 447 } 448 449 /** 450 * this won't be called since we don't do ICache logging here. 451 * <p> 452 * @return String 453 */ 454 @Override 455 public String getEventLoggingExtraInfo() 456 { 457 return "Lateral Cache No Wait"; 458 } 459 460 /** 461 * getStats 462 * @return String 463 */ 464 @Override 465 public String getStats() 466 { 467 return getStatistics().toString(); 468 } 469 470 /** 471 * @return IStats 472 */ 473 @Override 474 public IStats getStatistics() 475 { 476 final IStats stats = new Stats(); 477 stats.setTypeName( "Lateral Cache No Wait Facade" ); 478 479 final ArrayList<IStatElement<?>> elems = new ArrayList<>(); 480 481 if (noWaitMap != null) 482 { 483 elems.add(new StatElement<>("Number of No Waits", Integer.valueOf(noWaitMap.size()))); 484 485 elems.addAll(noWaitMap.values().stream() 486 .flatMap(lcnw -> lcnw.getStatistics().getStatElements().stream()) 487 .collect(Collectors.toList())); 488 } 489 490 stats.setStatElements( elems ); 491 492 return stats; 493 } 494}