001package org.apache.commons.jcs3.auxiliary.remote;
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.ArrayList;
024import java.util.Collections;
025import java.util.HashSet;
026import java.util.List;
027import java.util.Map;
028import java.util.Set;
029import java.util.stream.Collectors;
030
031import org.apache.commons.jcs3.auxiliary.AbstractAuxiliaryCache;
032import org.apache.commons.jcs3.auxiliary.remote.behavior.IRemoteCacheAttributes;
033import org.apache.commons.jcs3.engine.CacheStatus;
034import org.apache.commons.jcs3.engine.behavior.ICacheElement;
035import org.apache.commons.jcs3.engine.behavior.IElementSerializer;
036import org.apache.commons.jcs3.engine.logging.behavior.ICacheEventLogger;
037import org.apache.commons.jcs3.engine.stats.StatElement;
038import org.apache.commons.jcs3.engine.stats.Stats;
039import org.apache.commons.jcs3.engine.stats.behavior.IStatElement;
040import org.apache.commons.jcs3.engine.stats.behavior.IStats;
041import org.apache.commons.jcs3.log.Log;
042import org.apache.commons.jcs3.log.LogManager;
043
044/** An abstract base for the No Wait Facade.  Different implementations will failover differently. */
045public abstract class AbstractRemoteCacheNoWaitFacade<K, V>
046    extends AbstractAuxiliaryCache<K, V>
047{
048    /** log instance */
049    private static final Log log = LogManager.getLog( AbstractRemoteCacheNoWaitFacade.class );
050
051    /** The connection to a remote server, or a zombie. */
052    protected List<RemoteCacheNoWait<K, V>> noWaits;
053
054    /** holds failover and cluster information */
055    private final IRemoteCacheAttributes remoteCacheAttributes;
056
057    /**
058     * Constructs with the given remote cache, and fires events to any listeners.
059     * <p>
060     * @param noWaits
061     * @param rca
062     * @param cacheEventLogger
063     * @param elementSerializer
064     */
065    public AbstractRemoteCacheNoWaitFacade( final List<RemoteCacheNoWait<K,V>> noWaits, final IRemoteCacheAttributes rca,
066                                    final ICacheEventLogger cacheEventLogger, final IElementSerializer elementSerializer )
067    {
068        log.debug( "CONSTRUCTING NO WAIT FACADE" );
069        this.remoteCacheAttributes = rca;
070        setCacheEventLogger( cacheEventLogger );
071        setElementSerializer( elementSerializer );
072        this.noWaits = new ArrayList<>(noWaits);
073        for (final RemoteCacheNoWait<K,V> nw : this.noWaits)
074        {
075            // FIXME: This cast is very brave. Remove this.
076            ((RemoteCache<K, V>)nw.getRemoteCache()).setFacade(this);
077        }
078    }
079
080    /**
081     * Put an element in the cache.
082     * <p>
083     * @param ce
084     * @throws IOException
085     */
086    @Override
087    public void update( final ICacheElement<K, V> ce )
088        throws IOException
089    {
090        log.debug("updating through cache facade, noWaits.length = {0}", noWaits::size);
091
092        for (final RemoteCacheNoWait<K, V> nw : noWaits)
093        {
094            nw.update( ce );
095        }
096    }
097
098    /**
099     * Synchronously reads from the remote cache.
100     * <p>
101     * @param key
102     * @return Either an ICacheElement&lt;K, V&gt; or null if it is not found.
103     */
104    @Override
105    public ICacheElement<K, V> get( final K key ) throws IOException
106    {
107        for (final RemoteCacheNoWait<K, V> nw : noWaits)
108        {
109            return nw.get(key);
110        }
111
112        return null;
113    }
114
115    /**
116     * Synchronously read from the remote cache.
117     * <p>
118     * @param pattern
119     * @return map
120     * @throws IOException
121     */
122    @Override
123    public Map<K, ICacheElement<K, V>> getMatching( final String pattern )
124        throws IOException
125    {
126        for (final RemoteCacheNoWait<K, V> nw : noWaits)
127        {
128            return nw.getMatching( pattern );
129        }
130
131        return Collections.emptyMap();
132    }
133
134    /**
135     * Gets multiple items from the cache based on the given set of keys.
136     * <p>
137     * @param keys
138     * @return a map of K key to ICacheElement&lt;K, V&gt; element, or an empty map if there is no
139     *         data in cache for any of these keys
140     */
141    @Override
142    public Map<K, ICacheElement<K, V>> getMultiple( final Set<K> keys ) throws IOException
143    {
144        if ( keys != null && !keys.isEmpty() )
145        {
146            for (final RemoteCacheNoWait<K, V> nw : noWaits)
147            {
148                return nw.getMultiple( keys );
149            }
150        }
151
152        return Collections.emptyMap();
153    }
154
155    /**
156     * Return the keys in this cache.
157     * <p>
158     * @see org.apache.commons.jcs3.auxiliary.AuxiliaryCache#getKeySet()
159     */
160    @Override
161    public Set<K> getKeySet() throws IOException
162    {
163        final HashSet<K> allKeys = new HashSet<>();
164        for (final RemoteCacheNoWait<K, V> nw : noWaits)
165        {
166            final Set<K> keys = nw.getKeySet();
167            if(keys != null)
168            {
169                allKeys.addAll( keys );
170            }
171        }
172
173        return allKeys;
174    }
175
176    /**
177     * Adds a remove request to the remote cache.
178     * <p>
179     * @param key
180     * @return whether or not it was removed, right now it return false.
181     */
182    @Override
183    public boolean remove( final K key ) throws IOException
184    {
185        for (final RemoteCacheNoWait<K, V> nw : noWaits)
186        {
187            nw.remove( key );
188        }
189
190        return false;
191    }
192
193    /**
194     * Adds a removeAll request to the remote cache.
195     */
196    @Override
197    public void removeAll() throws IOException
198    {
199        for (final RemoteCacheNoWait<K, V> nw : noWaits)
200        {
201            nw.removeAll();
202        }
203    }
204
205    /** Adds a dispose request to the remote cache. */
206    @Override
207    public void dispose()
208    {
209        noWaits.forEach(RemoteCacheNoWait::dispose);
210    }
211
212    /**
213     * No remote invocation.
214     * <p>
215     * @return The size value
216     */
217    @Override
218    public int getSize()
219    {
220        return 0;
221        // cache.getSize();
222    }
223
224    /**
225     * Gets the cacheType attribute of the RemoteCacheNoWaitFacade object.
226     * <p>
227     * @return The cacheType value
228     */
229    @Override
230    public CacheType getCacheType()
231    {
232        return CacheType.REMOTE_CACHE;
233    }
234
235    /**
236     * Gets the cacheName attribute of the RemoteCacheNoWaitFacade object.
237     * <p>
238     * @return The cacheName value
239     */
240    @Override
241    public String getCacheName()
242    {
243        return remoteCacheAttributes.getCacheName();
244    }
245
246    /**
247     * Gets the status attribute of the RemoteCacheNoWaitFacade object
248     * <p>
249     * Return ALIVE if any are alive.
250     * <p>
251     * @return The status value
252     */
253    @Override
254    public CacheStatus getStatus()
255    {
256        return noWaits.stream()
257                .map(nw -> nw.getStatus())
258                .filter(status -> status == CacheStatus.ALIVE)
259                .findFirst()
260                .orElse(CacheStatus.DISPOSED);
261    }
262
263    /**
264     * String form of some of the configuration information for the remote cache.
265     * <p>
266     * @return Some info for logging.
267     */
268    @Override
269    public String toString()
270    {
271        return "RemoteCacheNoWaitFacade: " + remoteCacheAttributes.getCacheName() +
272                ", rca = " + remoteCacheAttributes;
273    }
274
275    /**
276     * Begin the failover process if this is a local cache. Clustered remote caches do not failover.
277     * <p>
278     * @param rcnw The no wait in error.
279     */
280    protected abstract void failover( RemoteCacheNoWait<K, V> rcnw );
281
282    /**
283     * Get the primary server from the list of failovers
284     *
285     * @return a no wait
286     */
287    public RemoteCacheNoWait<K, V> getPrimaryServer()
288    {
289        return noWaits.get(0);
290    }
291
292    /**
293     * restore the primary server in the list of failovers
294     *
295     */
296    public void restorePrimaryServer(final RemoteCacheNoWait<K, V> rcnw)
297    {
298        noWaits.clear();
299        noWaits.add(rcnw);
300    }
301
302    /**
303     * @return Returns the AuxiliaryCacheAttributes.
304     */
305    @Override
306    public IRemoteCacheAttributes getAuxiliaryCacheAttributes()
307    {
308        return this.remoteCacheAttributes;
309    }
310
311    /**
312     * getStats
313     * @return String
314     */
315    @Override
316    public String getStats()
317    {
318        return getStatistics().toString();
319    }
320
321    /**
322     * @return statistics about the cache region
323     */
324    @Override
325    public IStats getStatistics()
326    {
327        final IStats stats = new Stats();
328        stats.setTypeName( "Remote Cache No Wait Facade" );
329
330        final ArrayList<IStatElement<?>> elems = new ArrayList<>();
331
332        if ( noWaits != null )
333        {
334            elems.add(new StatElement<>( "Number of No Waits", Integer.valueOf(noWaits.size()) ) );
335
336            // get the stats from the super too
337            elems.addAll(noWaits.stream()
338                .flatMap(rcnw -> rcnw.getStatistics().getStatElements().stream())
339                .collect(Collectors.toList()));
340        }
341
342        stats.setStatElements( elems );
343
344        return stats;
345    }
346
347    /**
348     * This typically returns end point info .
349     * <p>
350     * @return the name
351     */
352    @Override
353    public String getEventLoggingExtraInfo()
354    {
355        return "Remote Cache No Wait Facade";
356    }
357}