001package org.apache.commons.jcs.auxiliary.remote;
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.Collections;
025import java.util.HashSet;
026import java.util.List;
027import java.util.Map;
028import java.util.Set;
029
030import org.apache.commons.jcs.auxiliary.AbstractAuxiliaryCache;
031import org.apache.commons.jcs.auxiliary.remote.behavior.IRemoteCacheAttributes;
032import org.apache.commons.jcs.engine.CacheStatus;
033import org.apache.commons.jcs.engine.behavior.ICache;
034import org.apache.commons.jcs.engine.behavior.ICacheElement;
035import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
036import org.apache.commons.jcs.engine.behavior.IElementSerializer;
037import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
038import org.apache.commons.jcs.engine.stats.StatElement;
039import org.apache.commons.jcs.engine.stats.Stats;
040import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
041import org.apache.commons.jcs.engine.stats.behavior.IStats;
042import org.apache.commons.logging.Log;
043import org.apache.commons.logging.LogFactory;
044
045/** An abstract base for the No Wait Facade.  Different implementations will failover differently. */
046public abstract class AbstractRemoteCacheNoWaitFacade<K, V>
047    extends AbstractAuxiliaryCache<K, V>
048{
049    /** log instance */
050    private static final Log log = LogFactory.getLog( AbstractRemoteCacheNoWaitFacade.class );
051
052    /** The connection to a remote server, or a zombie. */
053    protected List<RemoteCacheNoWait<K, V>> noWaits;
054
055    /** holds failover and cluster information */
056    private IRemoteCacheAttributes remoteCacheAttributes;
057
058    /**
059     * Constructs with the given remote cache, and fires events to any listeners.
060     * <p>
061     * @param noWaits
062     * @param rca
063     * @param cacheEventLogger
064     * @param elementSerializer
065     */
066    public AbstractRemoteCacheNoWaitFacade( List<RemoteCacheNoWait<K,V>> noWaits, IRemoteCacheAttributes rca,
067                                    ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
068    {
069        if ( log.isDebugEnabled() )
070        {
071            log.debug( "CONSTRUCTING NO WAIT FACADE" );
072        }
073        this.remoteCacheAttributes = rca;
074        setCacheEventLogger( cacheEventLogger );
075        setElementSerializer( elementSerializer );
076        this.noWaits = new ArrayList<RemoteCacheNoWait<K,V>>(noWaits);
077        for (RemoteCacheNoWait<K,V> nw : this.noWaits)
078        {
079            // FIXME: This cast is very brave. Remove this.
080            ((RemoteCache<K, V>)nw.getRemoteCache()).setFacade(this);
081        }
082    }
083
084    /**
085     * Constructs with the given remote cache, and fires events to any listeners.
086     * <p>
087     * @param noWaits
088     * @param rca
089     * @param cacheMgr
090     * @param cacheEventLogger
091     * @param elementSerializer
092     * @deprecated Unused parameter cacheMgr scheduled for removal
093     */
094    @Deprecated
095    public AbstractRemoteCacheNoWaitFacade( List<ICache<K, V>> noWaits, RemoteCacheAttributes rca,
096                                    ICompositeCacheManager cacheMgr, ICacheEventLogger cacheEventLogger,
097                                    IElementSerializer elementSerializer )
098    {
099        if ( log.isDebugEnabled() )
100        {
101            log.debug( "CONSTRUCTING NO WAIT FACADE" );
102        }
103        this.remoteCacheAttributes = rca;
104        setCacheEventLogger( cacheEventLogger );
105        setElementSerializer( elementSerializer );
106        this.noWaits = new ArrayList<RemoteCacheNoWait<K,V>>();
107        for (ICache<K, V> nw : noWaits)
108        {
109            RemoteCacheNoWait<K,V> rcnw = (RemoteCacheNoWait<K,V>)nw;
110            ((RemoteCache<K, V>)rcnw.getRemoteCache()).setFacade(this);
111            this.noWaits.add(rcnw);
112        }
113    }
114
115    /**
116     * Put an element in the cache.
117     * <p>
118     * @param ce
119     * @throws IOException
120     */
121    @Override
122    public void update( ICacheElement<K, V> ce )
123        throws IOException
124    {
125        if ( log.isDebugEnabled() )
126        {
127            log.debug( "updating through cache facade, noWaits.length = " + noWaits.size() );
128        }
129
130        for (RemoteCacheNoWait<K, V> nw : noWaits)
131        {
132            try
133            {
134                nw.update( ce );
135                // an initial move into a zombie will lock this to primary
136                // recovery. will not discover other servers until primary
137                // reconnect
138                // and subsequent error
139            }
140            catch ( IOException ex )
141            {
142                String message = "Problem updating no wait. Will initiate failover if the noWait is in error.";
143                log.error( message, ex );
144
145                if ( getCacheEventLogger() != null )
146                {
147                    getCacheEventLogger().logError( "RemoteCacheNoWaitFacade",
148                                                    ICacheEventLogger.UPDATE_EVENT,
149                                                    message + ":" + ex.getMessage() + " REGION: " + ce.getCacheName()
150                                                        + " ELEMENT: " + ce );
151                }
152
153                // can handle failover here? Is it safe to try the others?
154                // check to see it the noWait is now a zombie
155                // if it is a zombie, then move to the next in the failover list
156                // will need to keep them in order or a count
157                failover( nw );
158                // should start a failover thread
159                // should probably only failover if there is only one in the noWait
160                // list
161                // Should start a background thread to restore the original primary if we are in failover state.
162            }
163        }
164    }
165
166    /**
167     * Synchronously reads from the remote cache.
168     * <p>
169     * @param key
170     * @return Either an ICacheElement&lt;K, V&gt; or null if it is not found.
171     */
172    @Override
173    public ICacheElement<K, V> get( K key )
174    {
175        for (RemoteCacheNoWait<K, V> nw : noWaits)
176        {
177            try
178            {
179                ICacheElement<K, V> obj = nw.get( key );
180                if ( obj != null )
181                {
182                    return obj;
183                }
184            }
185            catch ( IOException ex )
186            {
187                log.debug( "Failed to get." );
188                return null;
189            }
190        }
191        return null;
192    }
193
194    /**
195     * Synchronously read from the remote cache.
196     * <p>
197     * @param pattern
198     * @return map
199     * @throws IOException
200     */
201    @Override
202    public Map<K, ICacheElement<K, V>> getMatching( String pattern )
203        throws IOException
204    {
205        for (RemoteCacheNoWait<K, V> nw : noWaits)
206        {
207            try
208            {
209                return nw.getMatching( pattern );
210            }
211            catch ( IOException ex )
212            {
213                log.debug( "Failed to getMatching." );
214            }
215        }
216        return Collections.emptyMap();
217    }
218
219    /**
220     * Gets multiple items from the cache based on the given set of keys.
221     * <p>
222     * @param keys
223     * @return a map of K key to ICacheElement&lt;K, V&gt; element, or an empty map if there is no
224     *         data in cache for any of these keys
225     */
226    @Override
227    public Map<K, ICacheElement<K, V>> getMultiple( Set<K> keys )
228    {
229        if ( keys != null && !keys.isEmpty() )
230        {
231            for (RemoteCacheNoWait<K, V> nw : noWaits)
232            {
233                try
234                {
235                    return nw.getMultiple( keys );
236                }
237                catch ( IOException ex )
238                {
239                    log.debug( "Failed to get." );
240                }
241            }
242        }
243
244        return Collections.emptyMap();
245    }
246
247    /**
248     * Return the keys in this cache.
249     * <p>
250     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet()
251     */
252    @Override
253    public Set<K> getKeySet() throws IOException
254    {
255        HashSet<K> allKeys = new HashSet<K>();
256        for (RemoteCacheNoWait<K, V> nw : noWaits)
257        {
258            if ( nw != null )
259            {
260                Set<K> keys = nw.getKeySet();
261                if(keys != null)
262                {
263                    allKeys.addAll( keys );
264                }
265            }
266        }
267        return allKeys;
268    }
269
270    /**
271     * Adds a remove request to the remote cache.
272     * <p>
273     * @param key
274     * @return whether or not it was removed, right now it return false.
275     */
276    @Override
277    public boolean remove( K key )
278    {
279        try
280        {
281            for (RemoteCacheNoWait<K, V> nw : noWaits)
282            {
283                nw.remove( key );
284            }
285        }
286        catch ( IOException ex )
287        {
288            log.error( ex );
289        }
290        return false;
291    }
292
293    /**
294     * Adds a removeAll request to the remote cache.
295     */
296    @Override
297    public void removeAll()
298    {
299        try
300        {
301            for (RemoteCacheNoWait<K, V> nw : noWaits)
302            {
303                nw.removeAll();
304            }
305        }
306        catch ( IOException ex )
307        {
308            log.error( ex );
309        }
310    }
311
312    /** Adds a dispose request to the remote cache. */
313    @Override
314    public void dispose()
315    {
316        for (RemoteCacheNoWait<K, V> nw : noWaits)
317        {
318            nw.dispose();
319        }
320    }
321
322    /**
323     * No remote invocation.
324     * <p>
325     * @return The size value
326     */
327    @Override
328    public int getSize()
329    {
330        return 0;
331        // cache.getSize();
332    }
333
334    /**
335     * Gets the cacheType attribute of the RemoteCacheNoWaitFacade object.
336     * <p>
337     * @return The cacheType value
338     */
339    @Override
340    public CacheType getCacheType()
341    {
342        return CacheType.REMOTE_CACHE;
343    }
344
345    /**
346     * Gets the cacheName attribute of the RemoteCacheNoWaitFacade object.
347     * <p>
348     * @return The cacheName value
349     */
350    @Override
351    public String getCacheName()
352    {
353        return remoteCacheAttributes.getCacheName();
354    }
355
356    /**
357     * Gets the status attribute of the RemoteCacheNoWaitFacade object
358     * <p>
359     * Return ALIVE if any are alive.
360     * <p>
361     * @return The status value
362     */
363    @Override
364    public CacheStatus getStatus()
365    {
366        for (RemoteCacheNoWait<K, V> nw : noWaits)
367        {
368            if ( nw.getStatus() == CacheStatus.ALIVE )
369            {
370                return CacheStatus.ALIVE;
371            }
372        }
373
374        return CacheStatus.DISPOSED;
375    }
376
377    /**
378     * String form of some of the configuration information for the remote cache.
379     * <p>
380     * @return Some info for logging.
381     */
382    @Override
383    public String toString()
384    {
385        return "RemoteCacheNoWaitFacade: " + remoteCacheAttributes.getCacheName() + ", rca = " + remoteCacheAttributes;
386    }
387
388    /**
389     * Begin the failover process if this is a local cache. Clustered remote caches do not failover.
390     * <p>
391     * @param rcnw The no wait in error.
392     */
393    protected abstract void failover( RemoteCacheNoWait<K, V> rcnw );
394
395    /**
396     * Get the primary server from the list of failovers
397     *
398     * @return a no wait
399     */
400    public RemoteCacheNoWait<K, V> getPrimaryServer()
401    {
402        return noWaits.get(0);
403    }
404
405    /**
406     * restore the primary server in the list of failovers
407     *
408     */
409    public void restorePrimaryServer(RemoteCacheNoWait<K, V> rcnw)
410    {
411        noWaits.clear();
412        noWaits.add(rcnw);
413    }
414
415    /**
416     * @return Returns the AuxiliaryCacheAttributes.
417     */
418    @Override
419    public IRemoteCacheAttributes getAuxiliaryCacheAttributes()
420    {
421        return this.remoteCacheAttributes;
422    }
423
424    /**
425     * getStats
426     * @return String
427     */
428    @Override
429    public String getStats()
430    {
431        return getStatistics().toString();
432    }
433
434    /**
435     * @return statistics about the cache region
436     */
437    @Override
438    public IStats getStatistics()
439    {
440        IStats stats = new Stats();
441        stats.setTypeName( "Remote Cache No Wait Facade" );
442
443        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
444
445        if ( noWaits != null )
446        {
447            elems.add(new StatElement<Integer>( "Number of No Waits", Integer.valueOf(noWaits.size()) ) );
448
449            for ( RemoteCacheNoWait<K, V> rcnw : noWaits )
450            {
451                // get the stats from the super too
452                IStats sStats = rcnw.getStatistics();
453                elems.addAll(sStats.getStatElements());
454            }
455        }
456
457        stats.setStatElements( elems );
458
459        return stats;
460    }
461
462    /**
463     * This typically returns end point info .
464     * <p>
465     * @return the name
466     */
467    @Override
468    public String getEventLoggingExtraInfo()
469    {
470        return "Remote Cache No Wait Facade";
471    }
472}