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.Collections;
024import java.util.Map;
025import java.util.Set;
026
027import org.apache.commons.jcs3.auxiliary.AbstractAuxiliaryCacheEventLogging;
028import org.apache.commons.jcs3.auxiliary.lateral.behavior.ILateralCacheAttributes;
029import org.apache.commons.jcs3.engine.CacheInfo;
030import org.apache.commons.jcs3.engine.CacheStatus;
031import org.apache.commons.jcs3.engine.ZombieCacheServiceNonLocal;
032import org.apache.commons.jcs3.engine.behavior.ICacheElement;
033import org.apache.commons.jcs3.engine.behavior.ICacheServiceNonLocal;
034import org.apache.commons.jcs3.engine.behavior.IZombie;
035import org.apache.commons.jcs3.engine.stats.Stats;
036import org.apache.commons.jcs3.engine.stats.behavior.IStats;
037import org.apache.commons.jcs3.log.Log;
038import org.apache.commons.jcs3.log.LogManager;
039
040/**
041 * Lateral distributor. Returns null on get by default. Net search not implemented.
042 */
043public class LateralCache<K, V>
044    extends AbstractAuxiliaryCacheEventLogging<K, V>
045{
046    /** The logger. */
047    private static final Log log = LogManager.getLog( LateralCache.class );
048
049    /** generalize this, use another interface */
050    private final ILateralCacheAttributes lateralCacheAttributes;
051
052    /** The region name */
053    final String cacheName;
054
055    /** either http, socket.udp, or socket.tcp can set in config */
056    private ICacheServiceNonLocal<K, V> lateralCacheService;
057
058    /** Monitors the connection. */
059    private LateralCacheMonitor monitor;
060
061    /**
062     * Constructor for the LateralCache object
063     * <p>
064     * @param cattr
065     * @param lateral
066     * @param monitor
067     */
068    public LateralCache( final ILateralCacheAttributes cattr, final ICacheServiceNonLocal<K, V> lateral, final LateralCacheMonitor monitor )
069    {
070        this.cacheName = cattr.getCacheName();
071        this.lateralCacheAttributes = cattr;
072        this.lateralCacheService = lateral;
073        this.monitor = monitor;
074    }
075
076    /**
077     * Constructor for the LateralCache object
078     * <p>
079     * @param cattr
080     *
081     * @deprecated Causes NPE
082     */
083    @Deprecated
084    public LateralCache( final ILateralCacheAttributes cattr )
085    {
086        this(cattr, null, null);
087    }
088
089    /**
090     * Update lateral.
091     * <p>
092     * @param ce
093     * @throws IOException
094     */
095    @Override
096    protected void processUpdate( final ICacheElement<K, V> ce )
097        throws IOException
098    {
099        try
100        {
101            if (ce != null)
102            {
103                log.debug( "update: lateral = [{0}], CacheInfo.listenerId = {1}",
104                        lateralCacheService, CacheInfo.listenerId );
105                lateralCacheService.update( ce, CacheInfo.listenerId );
106            }
107        }
108        catch ( final IOException ex )
109        {
110            handleException( ex, "Failed to put [" + ce.getKey() + "] to " + ce.getCacheName() + "@" + lateralCacheAttributes );
111        }
112    }
113
114    /**
115     * The performance costs are too great. It is not recommended that you enable lateral gets.
116     * <p>
117     * @param key
118     * @return ICacheElement&lt;K, V&gt; or null
119     * @throws IOException
120     */
121    @Override
122    protected ICacheElement<K, V> processGet( final K key )
123        throws IOException
124    {
125        ICacheElement<K, V> obj = null;
126
127        if ( !this.lateralCacheAttributes.getPutOnlyMode() )
128        {
129            try
130            {
131                obj = lateralCacheService.get( cacheName, key );
132            }
133            catch ( final Exception e )
134            {
135                log.error( e );
136                handleException( e, "Failed to get [" + key + "] from " + lateralCacheAttributes.getCacheName() + "@" + lateralCacheAttributes );
137            }
138        }
139
140        return obj;
141    }
142
143    /**
144     * @param pattern
145     * @return A map of K key to ICacheElement&lt;K, V&gt; element, or an empty map if there is no
146     *         data in cache for any of these keys
147     * @throws IOException
148     */
149    @Override
150    protected Map<K, ICacheElement<K, V>> processGetMatching( final String pattern )
151        throws IOException
152    {
153        Map<K, ICacheElement<K, V>> map = Collections.emptyMap();
154
155        if ( !this.lateralCacheAttributes.getPutOnlyMode() )
156        {
157            try
158            {
159                return lateralCacheService.getMatching( cacheName, pattern );
160            }
161            catch ( final IOException e )
162            {
163                log.error( e );
164                handleException( e, "Failed to getMatching [" + pattern + "] from " + lateralCacheAttributes.getCacheName() + "@" + lateralCacheAttributes );
165            }
166        }
167
168        return map;
169    }
170
171    /**
172     * Return the keys in this cache.
173     * <p>
174     * @see org.apache.commons.jcs3.auxiliary.AuxiliaryCache#getKeySet()
175     */
176    @Override
177    public Set<K> getKeySet() throws IOException
178    {
179        try
180        {
181            return lateralCacheService.getKeySet( cacheName );
182        }
183        catch ( final IOException ex )
184        {
185            handleException( ex, "Failed to get key set from " + lateralCacheAttributes.getCacheName() + "@"
186                + lateralCacheAttributes );
187        }
188        return Collections.emptySet();
189    }
190
191    /**
192     * Synchronously remove from the remote cache; if failed, replace the remote handle with a
193     * zombie.
194     * <p>
195     * @param key
196     * @return false always
197     * @throws IOException
198     */
199    @Override
200    protected boolean processRemove( final K key )
201        throws IOException
202    {
203        log.debug( "removing key: {0}", key );
204
205        try
206        {
207            lateralCacheService.remove( cacheName, key, CacheInfo.listenerId );
208        }
209        catch ( final IOException ex )
210        {
211            handleException( ex, "Failed to remove " + key + " from " + lateralCacheAttributes.getCacheName() + "@" + lateralCacheAttributes );
212        }
213        return false;
214    }
215
216    /**
217     * Synchronously removeAll from the remote cache; if failed, replace the remote handle with a
218     * zombie.
219     * <p>
220     * @throws IOException
221     */
222    @Override
223    protected void processRemoveAll()
224        throws IOException
225    {
226        try
227        {
228            lateralCacheService.removeAll( cacheName, CacheInfo.listenerId );
229        }
230        catch ( final IOException ex )
231        {
232            handleException( ex, "Failed to remove all from " + lateralCacheAttributes.getCacheName() + "@" + lateralCacheAttributes );
233        }
234    }
235
236    /**
237     * Synchronously dispose the cache. Not sure we want this.
238     * <p>
239     * @throws IOException
240     */
241    @Override
242    protected void processDispose()
243        throws IOException
244    {
245        log.debug( "Disposing of lateral cache" );
246
247        try
248        {
249            lateralCacheService.dispose( this.lateralCacheAttributes.getCacheName() );
250            // Should remove connection
251        }
252        catch ( final IOException ex )
253        {
254            log.error( "Couldn't dispose", ex );
255            handleException( ex, "Failed to dispose " + lateralCacheAttributes.getCacheName() );
256        }
257    }
258
259    /**
260     * Returns the cache status.
261     * <p>
262     * @return The status value
263     */
264    @Override
265    public CacheStatus getStatus()
266    {
267        return this.lateralCacheService instanceof IZombie ? CacheStatus.ERROR : CacheStatus.ALIVE;
268    }
269
270    /**
271     * Returns the current cache size.
272     * <p>
273     * @return The size value
274     */
275    @Override
276    public int getSize()
277    {
278        return 0;
279    }
280
281    /**
282     * Gets the cacheType attribute of the LateralCache object
283     * <p>
284     * @return The cacheType value
285     */
286    @Override
287    public CacheType getCacheType()
288    {
289        return CacheType.LATERAL_CACHE;
290    }
291
292    /**
293     * Gets the cacheName attribute of the LateralCache object
294     * <p>
295     * @return The cacheName value
296     */
297    @Override
298    public String getCacheName()
299    {
300        return cacheName;
301    }
302
303    /**
304     * Not yet sure what to do here.
305     * <p>
306     * @param ex
307     * @param msg
308     * @throws IOException
309     */
310    private void handleException( final Exception ex, final String msg )
311        throws IOException
312    {
313        log.error( "Disabling lateral cache due to error {0}", msg, ex );
314
315        lateralCacheService = new ZombieCacheServiceNonLocal<>( lateralCacheAttributes.getZombieQueueMaxSize() );
316        // may want to flush if region specifies
317        // Notify the cache monitor about the error, and kick off the recovery
318        // process.
319        monitor.notifyError();
320
321        // could stop the net search if it is built and try to reconnect?
322        if ( ex instanceof IOException )
323        {
324            throw (IOException) ex;
325        }
326        throw new IOException( ex.getMessage() );
327    }
328
329    /**
330     * Replaces the current remote cache service handle with the given handle.
331     * <p>
332     * @param restoredLateral
333     */
334    public void fixCache( final ICacheServiceNonLocal<K, V> restoredLateral )
335    {
336        if ( this.lateralCacheService instanceof ZombieCacheServiceNonLocal )
337        {
338            final ZombieCacheServiceNonLocal<K, V> zombie = (ZombieCacheServiceNonLocal<K, V>) this.lateralCacheService;
339            this.lateralCacheService = restoredLateral;
340            try
341            {
342                zombie.propagateEvents( restoredLateral );
343            }
344            catch ( final Exception e )
345            {
346                try
347                {
348                    handleException( e, "Problem propagating events from Zombie Queue to new Lateral Service." );
349                }
350                catch ( final IOException e1 )
351                {
352                    // swallow, since this is just expected kick back.  Handle always throws
353                }
354            }
355        }
356        else
357        {
358            this.lateralCacheService = restoredLateral;
359        }
360    }
361
362    /**
363     * getStats
364     * <p>
365     * @return String
366     */
367    @Override
368    public String getStats()
369    {
370        return "";
371    }
372
373    /**
374     * @return Returns the AuxiliaryCacheAttributes.
375     */
376    @Override
377    public ILateralCacheAttributes getAuxiliaryCacheAttributes()
378    {
379        return lateralCacheAttributes;
380    }
381
382    /**
383     * @return debugging data.
384     */
385    @Override
386    public String toString()
387    {
388        final StringBuilder buf = new StringBuilder();
389        buf.append( "\n LateralCache " );
390        buf.append( "\n Cache Name [" + lateralCacheAttributes.getCacheName() + "]" );
391        buf.append( "\n cattr =  [" + lateralCacheAttributes + "]" );
392        return buf.toString();
393    }
394
395    /**
396     * @return extra data.
397     */
398    @Override
399    public String getEventLoggingExtraInfo()
400    {
401        return null;
402    }
403
404    /**
405     * The NoWait on top does not call out to here yet.
406     * <p>
407     * @return almost nothing
408     */
409    @Override
410    public IStats getStatistics()
411    {
412        final IStats stats = new Stats();
413        stats.setTypeName( "LateralCache" );
414        return stats;
415    }
416}