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