blob: 6f8e24860fa75ca605f7a4f9c89704538b6eda39 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.chemistry.opencmis.client.bindings.cache.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.chemistry.opencmis.client.bindings.cache.Cache;
import org.apache.chemistry.opencmis.client.bindings.cache.CacheLevel;
import org.apache.chemistry.opencmis.commons.impl.ClassLoaderUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default cache implementation.
*/
public class CacheImpl implements Cache {
private static final Logger LOG = LoggerFactory.getLogger(CacheImpl.class);
private static final long serialVersionUID = 1L;
private List<Class<?>> levels;
private List<Map<String, String>> levelParameters;
private final String name;
private CacheLevel root;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
/**
* Constructor.
*/
public CacheImpl() {
this.name = "Cache";
}
/**
* Constructor.
*/
public CacheImpl(String name) {
this.name = name;
}
public void initialize(String[] cacheLevelConfig) {
if (levels != null) {
throw new IllegalStateException("Cache already initialize!");
}
if ((cacheLevelConfig == null) || (cacheLevelConfig.length == 0)) {
throw new IllegalArgumentException("Cache config must not be empty!");
}
lock.writeLock().lock();
try {
levels = new ArrayList<Class<?>>();
levelParameters = new ArrayList<Map<String, String>>();
// build level lists
for (String config : cacheLevelConfig) {
int x = config.indexOf(' ');
if (x == -1) {
addLevel(config, null);
} else {
addLevel(config.substring(0, x), config.substring(x + 1));
}
}
// create root
root = createCacheLevel(0);
} finally {
lock.writeLock().unlock();
}
}
private void addLevel(String className, String parameters) {
// get the class
Class<?> clazz;
try {
clazz = ClassLoaderUtil.loadClass(className, this.getClass().getClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Class '" + className + "' not found!", e);
}
// check the class
if (!CacheLevel.class.isAssignableFrom(clazz)) {
throw new IllegalArgumentException("Class '" + className + "' does not implement the CacheLevel interface!");
}
levels.add(clazz);
// process parameters
if (parameters == null) {
levelParameters.add(null);
} else {
Map<String, String> parameterMap = new HashMap<String, String>();
levelParameters.add(parameterMap);
for (String pair : parameters.split(",")) {
String[] keyValue = pair.split("=");
if (keyValue.length == 1) {
parameterMap.put(keyValue[0], "");
} else {
parameterMap.put(keyValue[0], keyValue[1]);
}
}
}
}
public Object get(String... keys) {
// check keys
if (keys == null) {
return null;
}
// check level depth
if (levels.size() != keys.length) {
throw new IllegalArgumentException("Wrong number of keys!");
}
Object result = null;
lock.readLock().lock();
try {
CacheLevel cacheLevel = root;
// follow the branch
for (int i = 0; i < keys.length - 1; i++) {
Object level = cacheLevel.get(keys[i]);
// does the branch exist?
if (level == null) {
return null;
}
// next level
cacheLevel = (CacheLevel) level;
}
// get the value
result = cacheLevel.get(keys[keys.length - 1]);
} finally {
lock.readLock().unlock();
}
return result;
}
public void put(Object value, String... keys) {
// check keys
if (keys == null) {
return;
}
// check level depth
if (levels.size() != keys.length) {
throw new IllegalArgumentException("Wrong number of keys!");
}
lock.writeLock().lock();
try {
CacheLevel cacheLevel = root;
// follow the branch
for (int i = 0; i < keys.length - 1; i++) {
Object level = cacheLevel.get(keys[i]);
// does the branch exist?
if (level == null) {
level = createCacheLevel(i + 1);
cacheLevel.put(level, keys[i]);
}
// next level
cacheLevel = (CacheLevel) level;
}
cacheLevel.put(value, keys[keys.length - 1]);
if (LOG.isTraceEnabled()) {
LOG.trace("{}: put [{}] = {}", name, getFormattedKeys(keys), value);
}
} finally {
lock.writeLock().unlock();
}
}
public void remove(String... keys) {
if (keys == null) {
return;
}
lock.writeLock().lock();
try {
CacheLevel cacheLevel = root;
// follow the branch
for (int i = 0; i < keys.length - 1; i++) {
Object level = cacheLevel.get(keys[i]);
// does the branch exist?
if (level == null) {
return;
}
// next level
cacheLevel = (CacheLevel) level;
}
cacheLevel.remove(keys[keys.length - 1]);
if (LOG.isTraceEnabled()) {
LOG.trace("{}: removed [{}]", name, getFormattedKeys(keys));
}
} finally {
lock.writeLock().unlock();
}
}
public void removeAll() {
lock.writeLock().lock();
try {
root = createCacheLevel(0);
if (LOG.isTraceEnabled()) {
LOG.trace("{}: removed all", name);
}
} finally {
lock.writeLock().unlock();
}
}
public int check(String... keys) {
if (keys == null) {
return -1;
}
lock.readLock().lock();
try {
CacheLevel cacheLevel = root;
// follow the branch
for (int i = 0; i < keys.length - 1; i++) {
Object level = cacheLevel.get(keys[i]);
// does the branch exist?
if (level == null) {
return i;
}
// next level
cacheLevel = (CacheLevel) level;
}
} finally {
lock.readLock().unlock();
}
return keys.length;
}
public void writeLock() {
lock.writeLock().lock();
}
public void writeUnlock() {
lock.writeLock().unlock();
}
// ---- internal ----
/**
* Creates a cache level object.
*/
private CacheLevel createCacheLevel(int level) {
if ((level < 0) || (level >= levels.size())) {
throw new IllegalArgumentException("Cache level doesn't fit the configuration!");
}
// get the class and create an instance
Class<?> clazz = levels.get(level);
CacheLevel cacheLevel = null;
try {
cacheLevel = (CacheLevel) clazz.newInstance();
} catch (Exception e) {
throw new IllegalArgumentException("Cache level problem?!", e);
}
// initialize it
cacheLevel.initialize(levelParameters.get(level));
return cacheLevel;
}
@Override
public String toString() {
return (root == null ? "(no cache root)" : root.toString());
}
// ---- internal ----
private static String getFormattedKeys(String[] keys) {
assert keys != null;
StringBuilder sb = new StringBuilder();
for (String k : keys) {
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(k);
}
return sb.toString();
}
}