001package org.apache.commons.jcs.auxiliary.remote.http.server;
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.io.Serializable;
024import java.util.Map;
025import java.util.Set;
026
027import org.apache.commons.jcs.engine.behavior.ICacheElement;
028import org.apache.commons.jcs.engine.behavior.ICacheServiceNonLocal;
029import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
030import org.apache.commons.jcs.engine.control.CompositeCache;
031import org.apache.commons.jcs.engine.logging.CacheEvent;
032import org.apache.commons.jcs.engine.logging.behavior.ICacheEvent;
033import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
034import org.apache.commons.logging.Log;
035import org.apache.commons.logging.LogFactory;
036
037/**
038 * This class contains common methods for remote cache services. Eventually I hope to extract out
039 * much of the RMI server to use this as well. I'm starting with the Http service.
040 */
041public abstract class AbstractRemoteCacheService<K, V>
042    implements ICacheServiceNonLocal<K, V>
043{
044    /** An optional event logger */
045    private transient ICacheEventLogger cacheEventLogger;
046
047    /** The central hub */
048    private ICompositeCacheManager cacheManager;
049
050    /** Name of the event log source. */
051    private String eventLogSourceName = "AbstractRemoteCacheService";
052
053    /** Number of puts into the cache. */
054    private int puts = 0;
055
056    /** The interval at which we will log updates. */
057    private final int logInterval = 100;
058
059    /** log instance */
060    private static final Log log = LogFactory.getLog( AbstractRemoteCacheService.class );
061
062    /**
063     * Creates the super with the needed items.
064     * <p>
065     * @param cacheManager
066     * @param cacheEventLogger
067     */
068    public AbstractRemoteCacheService( ICompositeCacheManager cacheManager, ICacheEventLogger cacheEventLogger )
069    {
070        this.cacheManager = cacheManager;
071        this.cacheEventLogger = cacheEventLogger;
072    }
073
074    /**
075     * @param item
076     * @throws IOException
077     */
078    @Override
079    public void update( ICacheElement<K, V> item )
080        throws IOException
081    {
082        update( item, 0 );
083    }
084
085    /**
086     * The internal processing is wrapped in event logging calls.
087     * <p>
088     * @param item
089     * @param requesterId
090     * @throws IOException
091     */
092    @Override
093    public void update( ICacheElement<K, V> item, long requesterId )
094        throws IOException
095    {
096        ICacheEvent<ICacheElement<K, V>> cacheEvent = createICacheEvent( item, requesterId, ICacheEventLogger.UPDATE_EVENT );
097        try
098        {
099            logUpdateInfo( item );
100
101            processUpdate( item, requesterId );
102        }
103        finally
104        {
105            logICacheEvent( cacheEvent );
106        }
107    }
108
109    /**
110     * The internal processing is wrapped in event logging calls.
111     * <p>
112     * @param item
113     * @param requesterId
114     * @throws IOException
115     */
116    abstract void processUpdate( ICacheElement<K, V> item, long requesterId )
117        throws IOException;
118
119    /**
120     * Log some details.
121     * <p>
122     * @param item
123     */
124    private void logUpdateInfo( ICacheElement<K, V> item )
125    {
126        if ( log.isInfoEnabled() )
127        {
128            // not thread safe, but it doesn't have to be accurate
129            puts++;
130            if ( puts % logInterval == 0 )
131            {
132                log.info( "puts = " + puts );
133            }
134        }
135
136        if ( log.isDebugEnabled() )
137        {
138            log.debug( "In update, put [" + item.getKey() + "] in [" + item.getCacheName() + "]" );
139        }
140    }
141
142    /**
143     * Returns a cache value from the specified remote cache; or null if the cache or key does not
144     * exist.
145     * <p>
146     * @param cacheName
147     * @param key
148     * @return ICacheElement
149     * @throws IOException
150     */
151    @Override
152    public ICacheElement<K, V> get( String cacheName, K key )
153        throws IOException
154    {
155        return this.get( cacheName, key, 0 );
156    }
157
158    /**
159     * Returns a cache bean from the specified cache; or null if the key does not exist.
160     * <p>
161     * Adding the requestor id, allows the cache to determine the source of the get.
162     * <p>
163     * The internal processing is wrapped in event logging calls.
164     * <p>
165     * @param cacheName
166     * @param key
167     * @param requesterId
168     * @return ICacheElement
169     * @throws IOException
170     */
171    @Override
172    public ICacheElement<K, V> get( String cacheName, K key, long requesterId )
173        throws IOException
174    {
175        ICacheElement<K, V> element = null;
176        ICacheEvent<K> cacheEvent = createICacheEvent( cacheName, key, requesterId, ICacheEventLogger.GET_EVENT );
177        try
178        {
179            element = processGet( cacheName, key, requesterId );
180        }
181        finally
182        {
183            logICacheEvent( cacheEvent );
184        }
185        return element;
186    }
187
188    /**
189     * Returns a cache bean from the specified cache; or null if the key does not exist.
190     * <p>
191     * Adding the requestor id, allows the cache to determine the source of the get.
192     * <p>
193     * @param cacheName
194     * @param key
195     * @param requesterId
196     * @return ICacheElement
197     * @throws IOException
198     */
199    abstract ICacheElement<K, V> processGet( String cacheName, K key, long requesterId )
200        throws IOException;
201
202    /**
203     * Gets all matching items.
204     * <p>
205     * @param cacheName
206     * @param pattern
207     * @return Map of keys and wrapped objects
208     * @throws IOException
209     */
210    @Override
211    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern )
212        throws IOException
213    {
214        return getMatching( cacheName, pattern, 0 );
215    }
216
217    /**
218     * Retrieves all matching keys.
219     * <p>
220     * @param cacheName
221     * @param pattern
222     * @param requesterId
223     * @return Map of keys and wrapped objects
224     * @throws IOException
225     */
226    @Override
227    public Map<K, ICacheElement<K, V>> getMatching( String cacheName, String pattern, long requesterId )
228        throws IOException
229    {
230        ICacheEvent<String> cacheEvent = createICacheEvent( cacheName, pattern, requesterId,
231                                                    ICacheEventLogger.GETMATCHING_EVENT );
232        try
233        {
234            return processGetMatching( cacheName, pattern, requesterId );
235        }
236        finally
237        {
238            logICacheEvent( cacheEvent );
239        }
240    }
241
242    /**
243     * Retrieves all matching keys.
244     * <p>
245     * @param cacheName
246     * @param pattern
247     * @param requesterId
248     * @return Map of keys and wrapped objects
249     * @throws IOException
250     */
251    abstract Map<K, ICacheElement<K, V>> processGetMatching( String cacheName, String pattern, long requesterId )
252        throws IOException;
253
254    /**
255     * Gets multiple items from the cache based on the given set of keys.
256     * <p>
257     * @param cacheName
258     * @param keys
259     * @return a map of K key to ICacheElement&lt;K, V&gt; element, or an empty map if there is no
260     *         data in cache for any of these keys
261     * @throws IOException
262     */
263    @Override
264    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys )
265        throws IOException
266    {
267        return this.getMultiple( cacheName, keys, 0 );
268    }
269
270    /**
271     * Gets multiple items from the cache based on the given set of keys.
272     * <p>
273     * The internal processing is wrapped in event logging calls.
274     * <p>
275     * @param cacheName
276     * @param keys
277     * @param requesterId
278     * @return a map of K key to ICacheElement&lt;K, V&gt; element, or an empty map if there is no
279     *         data in cache for any of these keys
280     * @throws IOException
281     */
282    @Override
283    public Map<K, ICacheElement<K, V>> getMultiple( String cacheName, Set<K> keys, long requesterId )
284        throws IOException
285    {
286        ICacheEvent<Serializable> cacheEvent = createICacheEvent( cacheName, (Serializable) keys, requesterId,
287                                                    ICacheEventLogger.GETMULTIPLE_EVENT );
288        try
289        {
290            return processGetMultiple( cacheName, keys, requesterId );
291        }
292        finally
293        {
294            logICacheEvent( cacheEvent );
295        }
296    }
297
298    /**
299     * Gets multiple items from the cache based on the given set of keys.
300     * <p>
301     * @param cacheName
302     * @param keys
303     * @param requesterId
304     * @return a map of K key to ICacheElement&lt;K, V&gt; element, or an empty map if there is no
305     *         data in cache for any of these keys
306     * @throws IOException
307     */
308    abstract Map<K, ICacheElement<K, V>> processGetMultiple( String cacheName, Set<K> keys, long requesterId )
309        throws IOException;
310
311    /**
312     * Return the keys in this cache.
313     * <p>
314     * @see org.apache.commons.jcs.auxiliary.AuxiliaryCache#getKeySet()
315     */
316    @Override
317    public Set<K> getKeySet( String cacheName )
318    {
319        return processGetKeySet( cacheName );
320    }
321
322    /**
323     * Gets the set of keys of objects currently in the cache.
324     * <p>
325     * @param cacheName
326     * @return Set
327     */
328    public Set<K> processGetKeySet( String cacheName )
329    {
330        CompositeCache<K, V> cache = getCacheManager().getCache( cacheName );
331
332        return cache.getKeySet();
333    }
334
335    /**
336     * Removes the given key from the specified remote cache. Defaults the listener id to 0.
337     * <p>
338     * @param cacheName
339     * @param key
340     * @throws IOException
341     */
342    @Override
343    public void remove( String cacheName, K key )
344        throws IOException
345    {
346        remove( cacheName, key, 0 );
347    }
348
349    /**
350     * Remove the key from the cache region and don't tell the source listener about it.
351     * <p>
352     * The internal processing is wrapped in event logging calls.
353     * <p>
354     * @param cacheName
355     * @param key
356     * @param requesterId
357     * @throws IOException
358     */
359    @Override
360    public void remove( String cacheName, K key, long requesterId )
361        throws IOException
362    {
363        ICacheEvent<K> cacheEvent = createICacheEvent( cacheName, key, requesterId, ICacheEventLogger.REMOVE_EVENT );
364        try
365        {
366            processRemove( cacheName, key, requesterId );
367        }
368        finally
369        {
370            logICacheEvent( cacheEvent );
371        }
372    }
373
374    /**
375     * Remove the key from the cache region and don't tell the source listener about it.
376     * <p>
377     * @param cacheName
378     * @param key
379     * @param requesterId
380     * @throws IOException
381     */
382    abstract void processRemove( String cacheName, K key, long requesterId )
383        throws IOException;
384
385    /**
386     * Remove all keys from the specified remote cache.
387     * <p>
388     * @param cacheName
389     * @throws IOException
390     */
391    @Override
392    public void removeAll( String cacheName )
393        throws IOException
394    {
395        removeAll( cacheName, 0 );
396    }
397
398    /**
399     * Remove all keys from the specified remote cache.
400     * <p>
401     * The internal processing is wrapped in event logging calls.
402     * <p>
403     * @param cacheName
404     * @param requesterId
405     * @throws IOException
406     */
407    @Override
408    public void removeAll( String cacheName, long requesterId )
409        throws IOException
410    {
411        ICacheEvent<String> cacheEvent = createICacheEvent( cacheName, "all", requesterId, ICacheEventLogger.REMOVEALL_EVENT );
412        try
413        {
414            processRemoveAll( cacheName, requesterId );
415        }
416        finally
417        {
418            logICacheEvent( cacheEvent );
419        }
420    }
421
422    /**
423     * Remove all keys from the specified remote cache.
424     * <p>
425     * @param cacheName
426     * @param requesterId
427     * @throws IOException
428     */
429    abstract void processRemoveAll( String cacheName, long requesterId )
430        throws IOException;
431
432    /**
433     * Frees the specified remote cache.
434     * <p>
435     * @param cacheName
436     * @throws IOException
437     */
438    @Override
439    public void dispose( String cacheName )
440        throws IOException
441    {
442        dispose( cacheName, 0 );
443    }
444
445    /**
446     * Frees the specified remote cache.
447     * <p>
448     * @param cacheName
449     * @param requesterId
450     * @throws IOException
451     */
452    public void dispose( String cacheName, long requesterId )
453        throws IOException
454    {
455        ICacheEvent<String> cacheEvent = createICacheEvent( cacheName, "none", requesterId, ICacheEventLogger.DISPOSE_EVENT );
456        try
457        {
458            processDispose( cacheName, requesterId );
459        }
460        finally
461        {
462            logICacheEvent( cacheEvent );
463        }
464    }
465
466    /**
467     * @param cacheName
468     * @param requesterId
469     * @throws IOException
470     */
471    abstract void processDispose( String cacheName, long requesterId )
472        throws IOException;
473
474    /**
475     * Gets the stats attribute of the RemoteCacheServer object.
476     * <p>
477     * @return The stats value
478     * @throws IOException
479     */
480    public String getStats()
481        throws IOException
482    {
483        return cacheManager.getStats();
484    }
485
486    /**
487     * Logs an event if an event logger is configured.
488     * <p>
489     * @param item
490     * @param requesterId
491     * @param eventName
492     * @return ICacheEvent
493     */
494    protected ICacheEvent<ICacheElement<K, V>> createICacheEvent( ICacheElement<K, V> item, long requesterId, String eventName )
495    {
496        if ( cacheEventLogger == null )
497        {
498            return new CacheEvent<ICacheElement<K, V>>();
499        }
500        String ipAddress = getExtraInfoForRequesterId( requesterId );
501        return cacheEventLogger.createICacheEvent( getEventLogSourceName(), item.getCacheName(), eventName, ipAddress,
502                                                   item );
503    }
504
505    /**
506     * Logs an event if an event logger is configured.
507     * <p>
508     * @param cacheName
509     * @param key
510     * @param requesterId
511     * @param eventName
512     * @return ICacheEvent
513     */
514    protected <T> ICacheEvent<T> createICacheEvent( String cacheName, T key, long requesterId, String eventName )
515    {
516        if ( cacheEventLogger == null )
517        {
518            return new CacheEvent<T>();
519        }
520        String ipAddress = getExtraInfoForRequesterId( requesterId );
521        return cacheEventLogger.createICacheEvent( getEventLogSourceName(), cacheName, eventName, ipAddress, key );
522    }
523
524    /**
525     * Logs an event if an event logger is configured.
526     * <p>
527     * @param source
528     * @param eventName
529     * @param optionalDetails
530     */
531    protected void logApplicationEvent( String source, String eventName, String optionalDetails )
532    {
533        if ( cacheEventLogger != null )
534        {
535            cacheEventLogger.logApplicationEvent( source, eventName, optionalDetails );
536        }
537    }
538
539    /**
540     * Logs an event if an event logger is configured.
541     * <p>
542     * @param cacheEvent
543     */
544    protected <T> void logICacheEvent( ICacheEvent<T> cacheEvent )
545    {
546        if ( cacheEventLogger != null )
547        {
548            cacheEventLogger.logICacheEvent( cacheEvent );
549        }
550    }
551
552    /**
553     * Ip address for the client, if one is stored.
554     * <p>
555     * Protected for testing.
556     * <p>
557     * @param requesterId
558     * @return String
559     */
560    protected abstract String getExtraInfoForRequesterId( long requesterId );
561
562    /**
563     * Allows it to be injected.
564     * <p>
565     * @param cacheEventLogger
566     */
567    public void setCacheEventLogger( ICacheEventLogger cacheEventLogger )
568    {
569        this.cacheEventLogger = cacheEventLogger;
570    }
571
572    /**
573     * @param cacheManager the cacheManager to set
574     */
575    protected void setCacheManager( ICompositeCacheManager cacheManager )
576    {
577        this.cacheManager = cacheManager;
578    }
579
580    /**
581     * @return the cacheManager
582     */
583    protected ICompositeCacheManager getCacheManager()
584    {
585        return cacheManager;
586    }
587
588    /**
589     * @param eventLogSourceName the eventLogSourceName to set
590     */
591    protected void setEventLogSourceName( String eventLogSourceName )
592    {
593        this.eventLogSourceName = eventLogSourceName;
594    }
595
596    /**
597     * @return the eventLogSourceName
598     */
599    protected String getEventLogSourceName()
600    {
601        return eventLogSourceName;
602    }
603}