View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.chemistry.opencmis.client.bindings.cache.impl;
20  
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.concurrent.locks.ReentrantReadWriteLock;
26  
27  import org.apache.chemistry.opencmis.client.bindings.cache.Cache;
28  import org.apache.chemistry.opencmis.client.bindings.cache.CacheLevel;
29  import org.apache.chemistry.opencmis.commons.impl.ClassLoaderUtil;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  /**
34   * Default cache implementation.
35   */
36  public class CacheImpl implements Cache {
37  
38      private static final Logger LOG = LoggerFactory.getLogger(CacheImpl.class);
39  
40      private static final long serialVersionUID = 1L;
41  
42      private List<Class<?>> levels;
43      private List<Map<String, String>> levelParameters;
44  
45      private final String name;
46  
47      private CacheLevel root;
48  
49      private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
50  
51      /**
52       * Constructor.
53       */
54      public CacheImpl() {
55          this.name = "Cache";
56      }
57  
58      /**
59       * Constructor.
60       */
61      public CacheImpl(String name) {
62          this.name = name;
63      }
64  
65      @Override
66      public void initialize(String[] cacheLevelConfig) {
67          if (levels != null) {
68              throw new IllegalStateException("Cache already initialize!");
69          }
70  
71          if (cacheLevelConfig == null || cacheLevelConfig.length == 0) {
72              throw new IllegalArgumentException("Cache config must not be empty!");
73          }
74  
75          lock.writeLock().lock();
76          try {
77              levels = new ArrayList<Class<?>>(cacheLevelConfig.length);
78              levelParameters = new ArrayList<Map<String, String>>();
79  
80              // build level lists
81              for (String config : cacheLevelConfig) {
82                  int x = config.indexOf(' ');
83                  if (x == -1) {
84                      addLevel(config, null);
85                  } else {
86                      addLevel(config.substring(0, x), config.substring(x + 1));
87                  }
88              }
89  
90              // create root
91              root = createCacheLevel(0);
92          } finally {
93              lock.writeLock().unlock();
94          }
95      }
96  
97      private void addLevel(String className, String parameters) {
98          // get the class
99          Class<?> clazz;
100         try {
101             clazz = ClassLoaderUtil.loadClass(className, this.getClass().getClassLoader());
102         } catch (ClassNotFoundException e) {
103             throw new IllegalArgumentException("Class '" + className + "' not found!", e);
104         }
105 
106         // check the class
107         if (!CacheLevel.class.isAssignableFrom(clazz)) {
108             throw new IllegalArgumentException("Class '" + className + "' does not implement the CacheLevel interface!");
109         }
110 
111         levels.add(clazz);
112 
113         // process parameters
114         if (parameters == null) {
115             levelParameters.add(null);
116         } else {
117             Map<String, String> parameterMap = new HashMap<String, String>();
118             levelParameters.add(parameterMap);
119 
120             for (String pair : parameters.split(",")) {
121                 String[] keyValue = pair.split("=");
122                 if (keyValue.length == 1) {
123                     parameterMap.put(keyValue[0], "");
124                 } else {
125                     parameterMap.put(keyValue[0], keyValue[1]);
126                 }
127             }
128         }
129     }
130 
131     @Override
132     public Object get(String... keys) {
133         // check keys
134         if (keys == null) {
135             return null;
136         }
137 
138         // check level depth
139         if (levels.size() != keys.length) {
140             throw new IllegalArgumentException("Wrong number of keys!");
141         }
142 
143         Object result = null;
144 
145         lock.readLock().lock();
146         try {
147             CacheLevel cacheLevel = root;
148 
149             // follow the branch
150             for (int i = 0; i < keys.length - 1; i++) {
151                 Object level = cacheLevel.get(keys[i]);
152 
153                 // does the branch exist?
154                 if (level == null) {
155                     return null;
156                 }
157 
158                 // next level
159                 cacheLevel = (CacheLevel) level;
160             }
161 
162             // get the value
163             result = cacheLevel.get(keys[keys.length - 1]);
164         } finally {
165             lock.readLock().unlock();
166         }
167 
168         return result;
169     }
170 
171     @Override
172     public void put(Object value, String... keys) {
173         // check keys
174         if (keys == null) {
175             return;
176         }
177 
178         // check level depth
179         if (levels.size() != keys.length) {
180             throw new IllegalArgumentException("Wrong number of keys!");
181         }
182 
183         lock.writeLock().lock();
184         try {
185             CacheLevel cacheLevel = root;
186 
187             // follow the branch
188             for (int i = 0; i < keys.length - 1; i++) {
189                 Object level = cacheLevel.get(keys[i]);
190 
191                 // does the branch exist?
192                 if (level == null) {
193                     level = createCacheLevel(i + 1);
194                     cacheLevel.put(level, keys[i]);
195                 }
196 
197                 // next level
198                 cacheLevel = (CacheLevel) level;
199             }
200 
201             cacheLevel.put(value, keys[keys.length - 1]);
202 
203             if (LOG.isTraceEnabled()) {
204                 LOG.trace("{}: put [{}] = {}", name, getFormattedKeys(keys), value);
205             }
206         } finally {
207             lock.writeLock().unlock();
208         }
209     }
210 
211     @Override
212     public void remove(String... keys) {
213         if (keys == null) {
214             return;
215         }
216 
217         lock.writeLock().lock();
218         try {
219             CacheLevel cacheLevel = root;
220 
221             // follow the branch
222             for (int i = 0; i < keys.length - 1; i++) {
223                 Object level = cacheLevel.get(keys[i]);
224 
225                 // does the branch exist?
226                 if (level == null) {
227                     return;
228                 }
229 
230                 // next level
231                 cacheLevel = (CacheLevel) level;
232             }
233 
234             cacheLevel.remove(keys[keys.length - 1]);
235 
236             if (LOG.isTraceEnabled()) {
237                 LOG.trace("{}: removed [{}]", name, getFormattedKeys(keys));
238             }
239         } finally {
240             lock.writeLock().unlock();
241         }
242     }
243 
244     @Override
245     public void removeAll() {
246         lock.writeLock().lock();
247         try {
248             root = createCacheLevel(0);
249 
250             if (LOG.isTraceEnabled()) {
251                 LOG.trace("{}: removed all", name);
252             }
253         } finally {
254             lock.writeLock().unlock();
255         }
256     }
257 
258     @Override
259     public int check(String... keys) {
260         if (keys == null) {
261             return -1;
262         }
263 
264         lock.readLock().lock();
265         try {
266             CacheLevel cacheLevel = root;
267 
268             // follow the branch
269             for (int i = 0; i < keys.length - 1; i++) {
270                 Object level = cacheLevel.get(keys[i]);
271 
272                 // does the branch exist?
273                 if (level == null) {
274                     return i;
275                 }
276 
277                 // next level
278                 cacheLevel = (CacheLevel) level;
279             }
280         } finally {
281             lock.readLock().unlock();
282         }
283 
284         return keys.length;
285     }
286 
287     @Override
288     public void writeLock() {
289         lock.writeLock().lock();
290     }
291 
292     @Override
293     public void writeUnlock() {
294         lock.writeLock().unlock();
295     }
296 
297     // ---- internal ----
298 
299     /**
300      * Creates a cache level object.
301      */
302     private CacheLevel createCacheLevel(int level) {
303         if (level < 0 || level >= levels.size()) {
304             throw new IllegalArgumentException("Cache level doesn't fit the configuration!");
305         }
306 
307         // get the class and create an instance
308         Class<?> clazz = levels.get(level);
309         CacheLevel cacheLevel = null;
310         try {
311             cacheLevel = (CacheLevel) clazz.newInstance();
312         } catch (Exception e) {
313             throw new IllegalArgumentException("Cache level problem?!", e);
314         }
315 
316         // initialize it
317         cacheLevel.initialize(levelParameters.get(level));
318 
319         return cacheLevel;
320     }
321 
322     @Override
323     public String toString() {
324         return root == null ? "(no cache root)" : root.toString();
325     }
326 
327     // ---- internal ----
328 
329     private static String getFormattedKeys(String[] keys) {
330         assert keys != null;
331 
332         StringBuilder sb = new StringBuilder(32);
333         for (String k : keys) {
334             if (sb.length() > 0) {
335                 sb.append(", ");
336             }
337             sb.append(k);
338         }
339 
340         return sb.toString();
341     }
342 }