View Javadoc
1   /*
2    * #%L
3    * wcm.io
4    * %%
5    * Copyright (C) 2014 wcm.io
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package io.wcm.caravan.pipeline.impl.cache;
21  
22  import io.wcm.caravan.pipeline.cache.CachePersistencyOptions;
23  import io.wcm.caravan.pipeline.cache.spi.CacheAdapter;
24  
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.List;
28  
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  import rx.Observable;
33  
34  /**
35   * Implementation of {@link CacheAdapter}.
36   * Wraps multiple references of different cache adapter instances.
37   * Delegates method calls to the subordinated cache adapters according to their priority.
38   */
39  public class MultiLayerCacheAdapter implements CacheAdapter {
40  
41    private static final Logger log = LoggerFactory.getLogger(MultiLayerCacheAdapter.class);
42  
43    /**
44     * Return multi layer cache key, if no cache key was returned by any child cache adapter.
45     */
46    public static final String MULTILAYER_CACHE_KEY = "NO_CACHEADAPTER_AVAILABLE";
47  
48    private List<CacheAdapter> cacheAdapters;
49  
50    /**
51     * Creates a multilayer cache adapter specifying list of cache adapter references. List of cache adapter references
52     * specifies the order and priority of caches, which will be requested.
53     * @param cacheAdapters List of {@link CacheAdapter} references
54     */
55    public MultiLayerCacheAdapter(List<CacheAdapter> cacheAdapters) {
56      this.cacheAdapters = new ArrayList<CacheAdapter>(cacheAdapters);
57  
58    }
59  
60    /**
61     * Retrieves cached item. Tries to retrieve item from the cache with the highest priority.
62     * If available item has been found in any next cache, such item will be stored into each cache with higher
63     * priority to read it earlier while next get attempt.
64     * @param cacheKey Cache key
65     * @param options valid cache persistency options
66     * @return an observable, than emits first found cache item. If no cache item is found, returns an empty observable.
67     */
68    @Override
69    public Observable<String> get(String cacheKey, CachePersistencyOptions options) {
70      return Observable.create(subscriber -> {
71        Observable<String> result = Observable.empty();
72        CacheAdapter actualCacheAdapter = null;
73        for (int i = 0; i < cacheAdapters.size(); i++) {
74          CacheAdapter cacheAdapter = cacheAdapters.get(i);
75          log.debug("Trying to retrieve document from cache level {} : {} : ", i, cacheAdapter.getClass().getSimpleName(), cacheKey);
76          result = cacheAdapter.get(cacheKey, options).cache();
77          if (!result.isEmpty().toBlocking().first()) {
78            log.debug("Retrieved document from cache level {} : {} : {}", i, cacheAdapter.getClass().getSimpleName(), cacheKey);
79            actualCacheAdapter = cacheAdapter;
80            break;
81          }
82        }
83        if (actualCacheAdapter != null) {
84          String cachedValue = result.toBlocking().first();
85          subscriber.onNext(cachedValue);
86          put(cacheKey, cachedValue, options, actualCacheAdapter);
87        }
88        subscriber.onCompleted();
89      });
90    }
91  
92    /*
93     * Puts cached item into the caches, which are specified to have higher priority and could be accessed while reading earlier than actual cache.
94     */
95    private void put(String cacheKey, String jsonString, CachePersistencyOptions options, CacheAdapter actualCacheAdapter) {
96      for (int i = 0; i < cacheAdapters.size(); i++) {
97        CacheAdapter cacheAdapter = cacheAdapters.get(i);
98        if (cacheAdapter != actualCacheAdapter) {
99          log.debug("Promoting document into cache level {} : {} : ", i, cacheAdapter.getClass().getSimpleName(), cacheKey);
100         cacheAdapter.put(cacheKey, jsonString, options);
101       }
102       else {
103         break;
104       }
105     }
106   }
107 
108   /**
109    * Store an item in each wrapped cache.
110    * @param cacheKey Cache key
111    * @param jsonString JSON data
112    * @param options valid cache persistency options
113    */
114   @Override
115   public void put(String cacheKey, String jsonString, CachePersistencyOptions options) {
116     for (int i = 0; i < cacheAdapters.size(); i++) {
117       CacheAdapter cacheAdapter = cacheAdapters.get(i);
118       log.debug("Putting document into cache level {} : {} : {} ", i, cacheAdapter.getClass().getSimpleName(), cacheKey);
119       cacheAdapter.put(cacheKey, jsonString, options);
120     }
121   }
122 
123   /**
124    * @return amount of registered caching levels
125    */
126   public int cachingLevels() {
127     return cacheAdapters.size();
128   }
129 
130   /**
131    * @return non modifiable list of cache adapters
132    */
133   public List<CacheAdapter> getCacheAdapters() {
134     return Collections.unmodifiableList(cacheAdapters);
135   }
136 
137 }