org.sakaiproject.nakamura.lite.storage.mem.MemoryStorageClient.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.nakamura.lite.storage.mem.MemoryStorageClient.java

Source

/*
 * Licensed to the Sakai Foundation (SF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The SF 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.sakaiproject.nakamura.lite.storage.mem;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import org.sakaiproject.nakamura.api.lite.RemoveProperty;
import org.sakaiproject.nakamura.api.lite.StorageClientException;
import org.sakaiproject.nakamura.api.lite.StorageClientUtils;
import org.sakaiproject.nakamura.api.lite.accesscontrol.AccessDeniedException;
import org.sakaiproject.nakamura.api.lite.content.Content;
import org.sakaiproject.nakamura.api.lite.util.PreemptiveIterator;
import org.sakaiproject.nakamura.lite.content.BlockContentHelper;
import org.sakaiproject.nakamura.lite.content.BlockSetContentHelper;
import org.sakaiproject.nakamura.lite.content.InternalContent;
import org.sakaiproject.nakamura.lite.storage.DisposableIterator;
import org.sakaiproject.nakamura.lite.storage.StorageClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class MemoryStorageClient implements StorageClient {

    private static final Logger LOGGER = LoggerFactory.getLogger(MemoryStorageClient.class);
    private static final Set<String> INDEX_COLUMNS = ImmutableSet.of("au:rep:principalName", "au:type");

    private static final Set<String> AUTO_INDEX_COLUMNS = ImmutableSet.of("cn:_:parenthash", "au:_:parenthash",
            "ac:_:parenthash");

    Map<String, Object> store;
    private int blockSize;
    private int maxChunksPerBlockSet;
    private BlockContentHelper contentHelper;
    private MemoryStorageClientPool pool;

    public MemoryStorageClient(MemoryStorageClientPool pool, Map<String, Object> store,
            Map<String, Object> properties) {
        this.store = store;
        this.pool = pool;
        contentHelper = new BlockSetContentHelper(this);
        blockSize = StorageClientUtils.getSetting(properties.get(BlockSetContentHelper.CONFIG_BLOCK_SIZE),
                BlockSetContentHelper.DEFAULT_BLOCK_SIZE);
        maxChunksPerBlockSet = StorageClientUtils.getSetting(
                properties.get(BlockSetContentHelper.CONFIG_MAX_CHUNKS_PER_BLOCK),
                BlockSetContentHelper.DEFAULT_MAX_CHUNKS_PER_BLOCK);

    }

    public void close() {
        pool.releaseClient(this);
    }

    public void destroy() {
    }

    public Map<String, Object> get(String keySpace, String columnFamily, String key) throws StorageClientException {
        return (Map<String, Object>) getOrCreateRow(keySpace, columnFamily, key);
    }

    private Map<String, Object> getOrCreateRow(String keySpace, String columnFamily, String key) {
        String keyName = rowHash(keySpace, columnFamily, key);

        if (!store.containsKey(keyName)) {
            Map<String, Object> row = Maps.newConcurrentHashMap();
            store.put(keyName, row);
            LOGGER.debug("Created {}  as {} ", new Object[] { keyName, row });
            return row;
        }
        @SuppressWarnings("unchecked")
        Map<String, Object> row = (Map<String, Object>) store.get(keyName);
        LOGGER.debug("Got {} as {} ", new Object[] { keyName, row });
        return row;
    }

    public String rowHash(String keySpace, String columnFamily, String key) {
        return keySpace + ":" + columnFamily + ":" + key;
    }

    public String keyHash(String keySpace, String columnFamily, String columnKey, Object columnValue) {
        return "_" + keySpace + ":" + columnFamily + ":" + columnKey + ":" + columnValue;
    }

    public void insert(String keySpace, String columnFamily, String key, Map<String, Object> values,
            boolean probablyNew) throws StorageClientException {
        Map<String, Object> row = get(keySpace, columnFamily, key);

        for (Entry<String, Object> e : values.entrySet()) {
            Object value = e.getValue();
            if (value instanceof byte[]) {
                byte[] bvalue = (byte[]) e.getValue();
                byte[] nvalue = new byte[bvalue.length];
                System.arraycopy(bvalue, 0, nvalue, 0, bvalue.length);
                value = nvalue;
            }
            if (value == null || value instanceof RemoveProperty) {
                Object previous = row.remove(e.getKey());
                removeIndex(keySpace, columnFamily, key, e.getKey(), previous);
            } else {
                Object previous = row.put(e.getKey(), value);
                removeIndex(keySpace, columnFamily, key, e.getKey(), previous);
                addIndex(keySpace, columnFamily, key, e.getKey(), e.getValue());
            }
        }
        LOGGER.debug("Updated {} {} ", key, row);
    }

    private void addIndex(String keySpace, String columnFamily, String key, String columnKey, Object value) {
        if (INDEX_COLUMNS.contains(columnFamily + ":" + columnKey)) {
            addIndexValue(keySpace, columnFamily, key, columnKey, value);
        }
        if (!StorageClientUtils.isRoot(key)) {
            addIndexValue(keySpace, columnFamily, key, InternalContent.PARENT_HASH_FIELD,
                    (rowHash(keySpace, columnFamily, StorageClientUtils.getParentObjectPath(key))));
        }
    }

    private void addIndexValue(String keySpace, String columnFamily, String key, String columnKey,
            Object columnValue) {
        String indexKey = keyHash(keySpace, columnFamily, columnKey, columnValue);
        @SuppressWarnings("unchecked")
        Set<String> index = (Set<String>) store.get(indexKey);
        if (index == null) {
            index = Sets.newConcurrentHashSet();
            store.put(indexKey, index);
        }
        index.add(rowHash(keySpace, columnFamily, key));
    }

    private void removeIndex(String keySpace, String columnFamily, String key, String columnKey,
            Object columnValue) {
        @SuppressWarnings("unchecked")
        Set<String> index = (Set<String>) store.get(keyHash(keySpace, columnFamily, columnKey, columnValue));
        if (index != null) {
            index.remove(rowHash(keySpace, columnFamily, key));
        }
    }

    public void remove(String keySpace, String columnFamily, String key) throws StorageClientException {
        String keyName = rowHash(keySpace, columnFamily, key);
        if (store.containsKey(keyName)) {
            @SuppressWarnings("unchecked")
            Map<String, Object> previous = (Map<String, Object>) store.remove(keyName);
            for (Entry<String, Object> e : previous.entrySet()) {
                removeIndex(keySpace, columnFamily, key, e.getKey(), e.getValue());
            }
        }
    }

    public Map<String, Object> streamBodyIn(String keySpace, String contentColumnFamily, String contentId,
            String contentBlockId, String streamId, Map<String, Object> content, InputStream in)
            throws StorageClientException, AccessDeniedException, IOException {
        return contentHelper.writeBody(keySpace, contentColumnFamily, contentId, contentBlockId, streamId,
                blockSize, maxChunksPerBlockSet, in);
    }

    public InputStream streamBodyOut(String keySpace, String contentColumnFamily, String contentId,
            String contentBlockId, String streamId, Map<String, Object> content)
            throws StorageClientException, AccessDeniedException {

        int nBlocks = toInt(content.get(Content.NBLOCKS_FIELD));
        return contentHelper.readBody(keySpace, contentColumnFamily, contentBlockId, streamId, nBlocks);
    }

    public boolean hasBody(Map<String, Object> content, String streamId) {
        return contentHelper.hasBody(content, streamId);
    }

    private int toInt(Object object) {
        if (object instanceof Integer) {
            return ((Integer) object).intValue();
        }
        return 0;
    }

    public DisposableIterator<Map<String, Object>> find(String keySpace, String columnFamily,
            Map<String, Object> properties) {
        List<Set<String>> matchingSets = Lists.newArrayList();
        for (Entry<String, Object> e : properties.entrySet()) {
            Object v = e.getValue();
            String k = e.getKey();
            if (shouldIndex(keySpace, columnFamily, k)) {
                if (v != null) {
                    @SuppressWarnings("unchecked")
                    Set<String> matches = (Set<String>) store
                            .get(keyHash(keySpace, columnFamily, e.getKey(), e.getValue()));
                    LOGGER.debug("Searching for {} found {} ",
                            keyHash(keySpace, columnFamily, e.getKey(), e.getValue()), matches);
                    if (matches != null) {
                        matchingSets.add(matches);
                    }
                }
            } else {
                LOGGER.warn("Search on {}:{} is not supported, filter dropped ", columnFamily, k);
            }
        }

        // find the union of all matching sets, using set views to build a tree of sets. This will be lazy iterating.
        Set<String> setOfRowHashes = null;
        for (Set<String> m : matchingSets) {
            if (setOfRowHashes == null) {
                setOfRowHashes = m;
            } else {
                setOfRowHashes = Sets.intersection(setOfRowHashes, m);
            }
        }
        LOGGER.debug("Matching Rowhashes is {} ", setOfRowHashes);

        Iterator<String> iterator = null;
        if (setOfRowHashes == null) {
            iterator = Iterators.emptyIterator();
        } else {
            iterator = setOfRowHashes.iterator();
        }
        final Iterator<String> matchedRowIds = iterator;
        return new PreemptiveIterator<Map<String, Object>>() {

            private Map<String, Object> nextMap;

            @SuppressWarnings("unchecked")
            @Override
            protected boolean internalHasNext() {
                while (matchedRowIds.hasNext()) {
                    nextMap = (Map<String, Object>) store.get(matchedRowIds.next());
                    if (nextMap != null) {
                        return true;
                    }
                }
                nextMap = null;
                close();
                return false;
            }

            @Override
            protected Map<String, Object> internalNext() {
                return nextMap;
            }
        };
    }

    private boolean shouldIndex(String keySpace, String columnFamily, String k) {
        if (AUTO_INDEX_COLUMNS.contains(columnFamily + ":" + k) || INDEX_COLUMNS.contains(columnFamily + ":" + k)) {
            return true;
        }
        return false;
    }

    public DisposableIterator<Map<String, Object>> listChildren(String keySpace, String columnFamily, String key)
            throws StorageClientException {
        String hash = rowHash(keySpace, columnFamily, key);
        LOGGER.debug("Finding {}:{}:{} as {} ", new Object[] { keySpace, columnFamily, key, hash });
        return find(keySpace, columnFamily, ImmutableMap.of(InternalContent.PARENT_HASH_FIELD, (Object) hash));
    }

}