001package org.apache.commons.jcs3.access;
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.HashMap;
024import java.util.Map;
025import java.util.Map.Entry;
026import java.util.Set;
027import java.util.function.Supplier;
028import java.util.stream.Collectors;
029
030import org.apache.commons.jcs3.access.behavior.ICacheAccess;
031import org.apache.commons.jcs3.access.exception.CacheException;
032import org.apache.commons.jcs3.access.exception.InvalidArgumentException;
033import org.apache.commons.jcs3.access.exception.InvalidHandleException;
034import org.apache.commons.jcs3.access.exception.ObjectExistsException;
035import org.apache.commons.jcs3.engine.CacheElement;
036import org.apache.commons.jcs3.engine.behavior.ICacheElement;
037import org.apache.commons.jcs3.engine.behavior.IElementAttributes;
038import org.apache.commons.jcs3.engine.control.CompositeCache;
039
040/**
041 * This class provides an interface for all types of access to the cache.
042 * <p>
043 * An instance of this class is tied to a specific cache region. Static methods are provided to get
044 * such instances.
045 * <p>
046 * Using this class you can retrieve an item, the item's wrapper, and the element's configuration.  You can also put an
047 * item in the cache, remove an item, and clear a region.
048 * <p>
049 * The JCS class is the preferred way to access these methods.
050 */
051public class CacheAccess<K, V>
052    extends AbstractCacheAccess<K, V>
053    implements ICacheAccess<K, V>
054{
055    /**
056     * Constructor for the CacheAccess object.
057     * <p>
058     * @param cacheControl The cache which the created instance accesses
059     */
060    public CacheAccess( final CompositeCache<K, V> cacheControl )
061    {
062        super(cacheControl);
063    }
064
065    /**
066     * Retrieve an object from the cache region this instance provides access to.
067     * <p>
068     * @param name Key the object is stored as
069     * @return The object if found or null
070     */
071    @Override
072    public V get( final K name )
073    {
074        final ICacheElement<K, V> element = this.getCacheControl().get( name );
075
076        return ( element != null ) ? element.getVal() : null;
077    }
078
079    /**
080     * Retrieve an object from the cache region this instance provides access to.
081     * If the object cannot be found in the cache, it will be retrieved by
082     * calling the supplier and subsequently storing it in the cache.
083     * <p>
084     * @param name
085     * @param supplier supplier to be called if the value is not found
086     * @return Object.
087     */
088    @Override
089    public V get(final K name, final Supplier<V> supplier)
090    {
091        V value = get(name);
092
093        if (value == null)
094        {
095            value = supplier.get();
096            put(name, value);
097        }
098
099        return value;
100    }
101
102    /**
103     * Retrieve matching objects from the cache region this instance provides access to.
104     * <p>
105     * @param pattern - a key pattern for the objects stored
106     * @return A map of key to values.  These are stripped from the wrapper.
107     */
108    @Override
109    public Map<K, V> getMatching( final String pattern )
110    {
111        final Map<K, V> unwrappedResults;
112
113        final Map<K, ICacheElement<K, V>> wrappedResults = this.getCacheControl().getMatching( pattern );
114
115        if ( wrappedResults == null )
116        {
117            unwrappedResults = new HashMap<>();
118        }
119        else
120        {
121            unwrappedResults = wrappedResults.entrySet()
122                    .stream()
123                    .filter(entry -> entry.getValue() != null)
124                    .collect(Collectors.toMap(
125                            Entry::getKey,
126                            entry -> entry.getValue().getVal()));
127        }
128
129        return unwrappedResults;
130    }
131
132    /**
133     * This method returns the ICacheElement&lt;K, V&gt; wrapper which provides access to element info and other
134     * attributes.
135     * <p>
136     * This returns a reference to the wrapper. Any modifications will be reflected in the cache. No
137     * defensive copy is made.
138     * <p>
139     * This method is most useful if you want to determine things such as the how long the element
140     * has been in the cache.
141     * <p>
142     * The last access time in the ElementAttributes should be current.
143     * <p>
144     * @param name Key the Serializable is stored as
145     * @return The ICacheElement&lt;K, V&gt; if the object is found or null
146     */
147    @Override
148    public ICacheElement<K, V> getCacheElement( final K name )
149    {
150        return this.getCacheControl().get( name );
151    }
152
153    /**
154     * Get multiple elements from the cache based on a set of cache keys.
155     * <p>
156     * This method returns the ICacheElement&lt;K, V&gt; wrapper which provides access to element info and other
157     * attributes.
158     * <p>
159     * This returns a reference to the wrapper. Any modifications will be reflected in the cache. No
160     * defensive copy is made.
161     * <p>
162     * This method is most useful if you want to determine things such as the how long the element
163     * has been in the cache.
164     * <p>
165     * The last access time in the ElementAttributes should be current.
166     * <p>
167     * @param names set of Serializable cache keys
168     * @return a map of K key to ICacheElement&lt;K, V&gt; element, or empty map if none of the keys are present
169     */
170    @Override
171    public Map<K, ICacheElement<K, V>> getCacheElements( final Set<K> names )
172    {
173        return this.getCacheControl().getMultiple( names );
174    }
175
176    /**
177     * Get multiple elements from the cache based on a set of cache keys.
178     * <p>
179     * This method returns the ICacheElement&lt;K, V&gt; wrapper which provides access to element info and other
180     * attributes.
181     * <p>
182     * This returns a reference to the wrapper. Any modifications will be reflected in the cache. No
183     * defensive copy is made.
184     * <p>
185     * This method is most useful if you want to determine things such as the how long the element
186     * has been in the cache.
187     * <p>
188     * The last access time in the ElementAttributes should be current.
189     * <p>
190     * @param pattern key search pattern
191     * @return a map of K key to ICacheElement&lt;K, V&gt; element, or empty map if no keys match the pattern
192     */
193    @Override
194    public Map<K, ICacheElement<K, V>> getMatchingCacheElements( final String pattern )
195    {
196        return this.getCacheControl().getMatching( pattern );
197    }
198
199    /**
200     * Place a new object in the cache, associated with key name. If there is currently an object
201     * associated with name in the region an ObjectExistsException is thrown. Names are scoped to a
202     * region so they must be unique within the region they are placed.
203     * <p>
204     * @param key Key object will be stored with
205     * @param value Object to store
206     * @throws CacheException and ObjectExistsException is thrown if the item is already in the
207     *                cache.
208     */
209    @Override
210    public void putSafe( final K key, final V value )
211    {
212        if ( this.getCacheControl().get( key ) != null )
213        {
214            throw new ObjectExistsException( "putSafe failed.  Object exists in the cache for key [" + key
215                + "].  Remove first or use a non-safe put to override the value." );
216        }
217        put( key, value );
218    }
219
220    /**
221     * Place a new object in the cache, associated with key name. If there is currently an object
222     * associated with name in the region it is replaced. Names are scoped to a region so they must
223     * be unique within the region they are placed.
224     * @param name Key object will be stored with
225     * @param obj Object to store
226     */
227    @Override
228    public void put( final K name, final V obj )
229    {
230        // Call put with a copy of the contained caches default attributes.
231        // the attributes are copied by the cacheControl
232        put( name, obj, this.getCacheControl().getElementAttributes() );
233    }
234
235    /**
236     * Constructs a cache element with these attributes, and puts it into the cache.
237     * <p>
238     * If the key or the value is null, and InvalidArgumentException is thrown.
239     * <p>
240     * @see org.apache.commons.jcs3.access.behavior.ICacheAccess#put(Object, Object, IElementAttributes)
241     */
242    @Override
243    public void put( final K key, final V val, final IElementAttributes attr )
244    {
245        if ( key == null )
246        {
247            throw new InvalidArgumentException( "Key must not be null" );
248        }
249
250        if ( val == null )
251        {
252            throw new InvalidArgumentException( "Value must not be null" );
253        }
254
255        // Create the element and update. This may throw an IOException which
256        // should be wrapped by cache access.
257        try
258        {
259            final CacheElement<K, V> ce = new CacheElement<>( this.getCacheControl().getCacheName(), key,
260                                                val );
261
262            ce.setElementAttributes( attr );
263
264            this.getCacheControl().update( ce );
265        }
266        catch ( final IOException e )
267        {
268            throw new CacheException( e );
269        }
270    }
271
272    /**
273     * Removes a single item by name.
274     * <p>
275     * @param name the name of the item to remove.
276     */
277    @Override
278    public void remove( final K name )
279    {
280        this.getCacheControl().remove( name );
281    }
282
283    /**
284     * Reset attributes for a particular element in the cache. NOTE: this method is currently not
285     * implemented.
286     * <p>
287     * @param name Key of object to reset attributes for
288     * @param attr New attributes for the object
289     * @throws InvalidHandleException if the item does not exist.
290     */
291    @Override
292    public void resetElementAttributes( final K name, final IElementAttributes attr )
293    {
294        final ICacheElement<K, V> element = this.getCacheControl().get( name );
295
296        if ( element == null )
297        {
298            throw new InvalidHandleException( "Object for name [" + name + "] is not in the cache" );
299        }
300
301        // Although it will work currently, don't assume pass by reference here,
302        // i.e. don't do this:
303        // element.setElementAttributes( attr );
304        // Another reason to call put is to force the changes to be distributed.
305
306        put( element.getKey(), element.getVal(), attr );
307    }
308
309    /**
310     * GetElementAttributes will return an attribute object describing the current attributes
311     * associated with the object name. The name object must override the Object.equals and
312     * Object.hashCode methods.
313     * <p>
314     * @param name Key of object to get attributes for
315     * @return Attributes for the object, null if object not in cache
316     */
317    @Override
318    public IElementAttributes getElementAttributes( final K name ) throws CacheException
319    {
320        IElementAttributes attr = null;
321
322        try
323        {
324            attr = this.getCacheControl().getElementAttributes( name );
325        }
326        catch ( final IOException ioe )
327        {
328            throw new CacheException("Failure getting element attributes", ioe);
329        }
330
331        return attr;
332    }
333}