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