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.AuxiliaryCacheAttributes;
024import org.apache.commons.jcs.engine.CacheAdaptor;
025import org.apache.commons.jcs.engine.CacheEventQueueFactory;
026import org.apache.commons.jcs.engine.CacheInfo;
027import org.apache.commons.jcs.engine.CacheStatus;
028import org.apache.commons.jcs.engine.behavior.ICacheElement;
029import org.apache.commons.jcs.engine.behavior.ICacheEventQueue;
030import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
031import org.apache.commons.jcs.engine.stats.StatElement;
032import org.apache.commons.jcs.engine.stats.Stats;
033import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
034import org.apache.commons.jcs.engine.stats.behavior.IStats;
035import org.apache.commons.logging.Log;
036import org.apache.commons.logging.LogFactory;
037
038import java.io.IOException;
039import java.rmi.UnmarshalException;
040import java.util.ArrayList;
041import java.util.Collections;
042import java.util.HashMap;
043import java.util.Map;
044import java.util.Set;
045
046/**
047 * Used to queue up update requests to the underlying cache. These requests will be processed in
048 * their order of arrival via the cache event queue processor.
049 */
050public class LateralCacheNoWait<K, V>
051    extends AbstractAuxiliaryCache<K, V>
052{
053    /** The logger. */
054    private static final Log log = LogFactory.getLog( LateralCacheNoWait.class );
055
056    /** The cache */
057    private final LateralCache<K, V> cache;
058
059    /** The event queue */
060    private ICacheEventQueue<K, V> eventQueue;
061
062    /** times get called */
063    private int getCount = 0;
064
065    /** times remove called */
066    private int removeCount = 0;
067
068    /** times put called */
069    private int putCount = 0;
070
071    /**
072     * Constructs with the given lateral cache, and fires up an event queue for asynchronous
073     * processing.
074     * <p>
075     * @param cache
076     */
077    public LateralCacheNoWait( LateralCache<K, V> cache )
078    {
079        this.cache = cache;
080
081        if ( log.isDebugEnabled() )
082        {
083            log.debug( "Constructing LateralCacheNoWait, LateralCache = [" + cache + "]" );
084        }
085
086        CacheEventQueueFactory<K, V> fact = new CacheEventQueueFactory<K, V>();
087        this.eventQueue = fact.createCacheEventQueue( new CacheAdaptor<K, V>( cache ), CacheInfo.listenerId, cache
088            .getCacheName(), cache.getAuxiliaryCacheAttributes().getEventQueuePoolName(), cache
089            .getAuxiliaryCacheAttributes().getEventQueueType() );
090
091        // need each no wait to handle each of its real updates and removes,
092        // since there may
093        // be more than one per cache? alternative is to have the cache
094        // perform updates using a different method that specifies the listener
095        // this.q = new CacheEventQueue(new CacheAdaptor(this),
096        // LateralCacheInfo.listenerId, cache.getCacheName());
097        if ( cache.getStatus() == CacheStatus.ERROR )
098        {
099            eventQueue.destroy();
100        }
101    }
102
103    /**
104     * @param ce
105     * @throws IOException
106     */
107    @Override
108    public void update( ICacheElement<K, V> ce )
109        throws IOException
110    {
111        putCount++;
112        try
113        {
114            eventQueue.addPutEvent( ce );
115        }
116        catch ( IOException ex )
117        {
118            log.error( ex );
119            eventQueue.destroy();
120        }
121    }
122
123    /**
124     * Synchronously reads from the lateral cache.
125     * <p>
126     * @param key
127     * @return ICacheElement&lt;K, V&gt; if found, else null
128     */
129    @Override
130    public ICacheElement<K, V> get( K key )
131    {
132        getCount++;
133        if ( this.getStatus() != CacheStatus.ERROR )
134        {
135            try
136            {
137                return cache.get( key );
138            }
139            catch ( UnmarshalException ue )
140            {
141                log.debug( "Retrying the get owing to UnmarshalException..." );
142                try
143                {
144                    return cache.get( key );
145                }
146                catch ( IOException ex )
147                {
148                    log.error( "Failed in retrying the get for the second time." );
149                    eventQueue.destroy();
150                }
151            }
152            catch ( IOException ex )
153            {
154                eventQueue.destroy();
155            }
156        }
157        return null;
158    }
159
160    /**
161     * Gets multiple items from the cache based on the given set of keys.
162     * <p>
163     * @param keys
164     * @return a map of K key to ICacheElement&lt;K, V&gt; element, or an empty map if there is no
165     *         data in cache for any of these keys
166     */
167    @Override
168    public Map<K, ICacheElement<K, V>> getMultiple(Set<K> keys)
169    {
170        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();
171
172        if ( keys != null && !keys.isEmpty() )
173        {
174            for (K key : keys)
175            {
176                ICacheElement<K, V> element = get( key );
177
178                if ( element != null )
179                {
180                    elements.put( key, element );
181                }
182            }
183        }
184
185        return elements;
186    }
187
188    /**
189     * Synchronously reads from the lateral cache.
190     * <p>
191     * @param pattern
192     * @return ICacheElement&lt;K, V&gt; if found, else empty
193     */
194    @Override
195    public Map<K, ICacheElement<K, V>> getMatching(String pattern)
196    {
197        getCount++;
198        if ( this.getStatus() != CacheStatus.ERROR )
199        {
200            try
201            {
202                return cache.getMatching( pattern );
203            }
204            catch ( UnmarshalException ue )
205            {
206                log.debug( "Retrying the get owing to UnmarshalException." );
207                try
208                {
209                    return cache.getMatching( pattern );
210                }
211                catch ( IOException ex )
212                {
213                    log.error( "Failed in retrying the get for the second time." );
214                    eventQueue.destroy();
215                }
216            }
217            catch ( IOException ex )
218            {
219                eventQueue.destroy();
220            }
221        }
222        return Collections.emptyMap();
223    }
224
225    /**
226     * Return the keys in this cache.
227     * <p>
228     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet()
229     */
230    @Override
231    public Set<K> getKeySet() throws IOException
232    {
233        try
234        {
235            return cache.getKeySet();
236        }
237        catch ( IOException ex )
238        {
239            log.error( ex );
240            eventQueue.destroy();
241        }
242        return Collections.emptySet();
243    }
244
245    /**
246     * Adds a remove request to the lateral cache.
247     * <p>
248     * @param key
249     * @return always false
250     */
251    @Override
252    public boolean remove( K key )
253    {
254        removeCount++;
255        try
256        {
257            eventQueue.addRemoveEvent( key );
258        }
259        catch ( IOException ex )
260        {
261            log.error( ex );
262            eventQueue.destroy();
263        }
264        return false;
265    }
266
267    /** Adds a removeAll request to the lateral cache. */
268    @Override
269    public void removeAll()
270    {
271        try
272        {
273            eventQueue.addRemoveAllEvent();
274        }
275        catch ( IOException ex )
276        {
277            log.error( ex );
278            eventQueue.destroy();
279        }
280    }
281
282    /** Adds a dispose request to the lateral cache. */
283    @Override
284    public void dispose()
285    {
286        try
287        {
288            eventQueue.addDisposeEvent();
289        }
290        catch ( IOException ex )
291        {
292            log.error( ex );
293            eventQueue.destroy();
294        }
295    }
296
297    /**
298     * No lateral invocation.
299     * <p>
300     * @return The size value
301     */
302    @Override
303    public int getSize()
304    {
305        return cache.getSize();
306    }
307
308    /**
309     * No lateral invocation.
310     * <p>
311     * @return The cacheType value
312     */
313    @Override
314    public CacheType getCacheType()
315    {
316        return cache.getCacheType();
317    }
318
319    /**
320     * Returns the asyn cache status. An error status indicates either the lateral connection is not
321     * available, or the asyn queue has been unexpectedly destroyed. No lateral invocation.
322     * <p>
323     * @return The status value
324     */
325    @Override
326    public CacheStatus getStatus()
327    {
328        return eventQueue.isWorking() ? cache.getStatus() : CacheStatus.ERROR;
329    }
330
331    /**
332     * Gets the cacheName attribute of the LateralCacheNoWait object
333     * <p>
334     * @return The cacheName value
335     */
336    @Override
337    public String getCacheName()
338    {
339        return cache.getCacheName();
340    }
341
342    /**
343     * Replaces the lateral cache service handle with the given handle and reset the queue by
344     * starting up a new instance.
345     * <p>
346     * @param lateral
347     */
348    public void fixCache( ICacheServiceNonLocal<K, V> lateral )
349    {
350        cache.fixCache( lateral );
351        resetEventQ();
352    }
353
354    /**
355     * Resets the event q by first destroying the existing one and starting up new one.
356     */
357    public void resetEventQ()
358    {
359        if ( eventQueue.isWorking() )
360        {
361            eventQueue.destroy();
362        }
363        CacheEventQueueFactory<K, V> fact = new CacheEventQueueFactory<K, V>();
364        this.eventQueue = fact.createCacheEventQueue( new CacheAdaptor<K, V>( cache ), CacheInfo.listenerId, cache
365            .getCacheName(), cache.getAuxiliaryCacheAttributes().getEventQueuePoolName(), cache
366            .getAuxiliaryCacheAttributes().getEventQueueType() );
367    }
368
369    /**
370     * @return Returns the AuxiliaryCacheAttributes.
371     */
372    @Override
373    public AuxiliaryCacheAttributes getAuxiliaryCacheAttributes()
374    {
375        return cache.getAuxiliaryCacheAttributes();
376    }
377
378    /**
379     * getStats
380     * @return String
381     */
382    @Override
383    public String getStats()
384    {
385        return getStatistics().toString();
386    }
387
388    /**
389     * this won't be called since we don't do ICache logging here.
390     * <p>
391     * @return String
392     */
393    @Override
394    public String getEventLoggingExtraInfo()
395    {
396        return "Lateral Cache No Wait";
397    }
398
399    /**
400     * @return statistics about this communication
401     */
402    @Override
403    public IStats getStatistics()
404    {
405        IStats stats = new Stats();
406        stats.setTypeName( "Lateral Cache No Wait" );
407
408        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
409
410        // get the stats from the event queue too
411        IStats eqStats = this.eventQueue.getStatistics();
412        elems.addAll(eqStats.getStatElements());
413
414        elems.add(new StatElement<Integer>( "Get Count", Integer.valueOf(this.getCount) ) );
415        elems.add(new StatElement<Integer>( "Remove Count", Integer.valueOf(this.removeCount) ) );
416        elems.add(new StatElement<Integer>( "Put Count", Integer.valueOf(this.putCount) ) );
417        elems.add(new StatElement<AuxiliaryCacheAttributes>( "Attributes", cache.getAuxiliaryCacheAttributes() ) );
418
419        stats.setStatElements( elems );
420
421        return stats;
422    }
423
424    /**
425     * @return debugging info.
426     */
427    @Override
428    public String toString()
429    {
430        StringBuilder buf = new StringBuilder();
431        buf.append( " LateralCacheNoWait " );
432        buf.append( " Status = " + this.getStatus() );
433        buf.append( " cache = [" + cache.toString() + "]" );
434        return buf.toString();
435    }
436}