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