View Javadoc
1   package org.apache.commons.jcs3.auxiliary.lateral;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Map.Entry;
30  import java.util.Objects;
31  import java.util.Set;
32  import java.util.concurrent.ConcurrentHashMap;
33  import java.util.concurrent.atomic.AtomicBoolean;
34  import java.util.stream.Collectors;
35  
36  import org.apache.commons.jcs3.auxiliary.AbstractAuxiliaryCache;
37  import org.apache.commons.jcs3.auxiliary.lateral.behavior.ILateralCacheAttributes;
38  import org.apache.commons.jcs3.auxiliary.lateral.behavior.ILateralCacheListener;
39  import org.apache.commons.jcs3.engine.CacheStatus;
40  import org.apache.commons.jcs3.engine.behavior.ICacheElement;
41  import org.apache.commons.jcs3.engine.stats.StatElement;
42  import org.apache.commons.jcs3.engine.stats.Stats;
43  import org.apache.commons.jcs3.engine.stats.behavior.IStatElement;
44  import org.apache.commons.jcs3.engine.stats.behavior.IStats;
45  import org.apache.commons.jcs3.log.Log;
46  import org.apache.commons.jcs3.log.LogManager;
47  
48  /**
49   * Used to provide access to multiple services under nowait protection. Composite factory should
50   * construct LateralCacheNoWaitFacade to give to the composite cache out of caches it constructs
51   * from the varies manager to lateral services. Perhaps the lateralcache factory should be able to
52   * do this.
53   */
54  public class LateralCacheNoWaitFacade<K, V>
55      extends AbstractAuxiliaryCache<K, V>
56  {
57      /** The logger */
58      private static final Log log = LogManager.getLog( LateralCacheNoWaitFacade.class );
59  
60      /**
61       * The queuing facade to the client.
62       * @deprecated Should not have been public in the first place
63       */
64      @Deprecated
65      public LateralCacheNoWait<K, V>[] noWaits;
66  
67      /**
68       * The queuing facade to the client.
69       */
70      private final ConcurrentHashMap<String, LateralCacheNoWait<K, V>> noWaitMap;
71  
72      /** The region name */
73      private final String cacheName;
74  
75      /** A cache listener */
76      private ILateralCacheListener<K, V> listener;
77  
78      /** User configurable attributes. */
79      private final ILateralCacheAttributes lateralCacheAttributes;
80  
81      /** Disposed state of this facade */
82      private final AtomicBoolean disposed = new AtomicBoolean(false);
83  
84      /**
85       * Constructs with the given lateral cache, and fires events to any listeners.
86       * <p>
87       * @param listener the cache listener
88       * @param noWaits the array of noWaits
89       * @param cattr the configuration
90       *
91       * @deprecated Use list constructor
92       */
93      @Deprecated
94      public LateralCacheNoWaitFacade(final ILateralCacheListener<K, V> listener, final LateralCacheNoWait<K, V>[] noWaits, final ILateralCacheAttributes cattr )
95      {
96          this(listener, Arrays.asList(noWaits), cattr);
97      }
98  
99      /**
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&lt;K, V&gt; 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 }