org.sdnplatform.sync.internal.store.StoreUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.sdnplatform.sync.internal.store.StoreUtils.java

Source

/*
 * Copyright 2008-2009 LinkedIn, Inc
 *
 * Licensed 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.sdnplatform.sync.internal.store;

import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.sdnplatform.sync.IClosableIterator;
import org.sdnplatform.sync.IVersion;
import org.sdnplatform.sync.Versioned;
import org.sdnplatform.sync.IVersion.Occurred;
import org.sdnplatform.sync.error.SyncException;
import org.sdnplatform.sync.internal.version.VectorClock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * Group of store utilities
 *
 */
public class StoreUtils {
    protected static final Logger logger = LoggerFactory.getLogger(StoreUtils.class);

    public static void assertValidKeys(Iterable<?> keys) {
        if (keys == null)
            throw new IllegalArgumentException("Keys cannot be null.");
        for (Object key : keys)
            assertValidKey(key);
    }

    public static <K> void assertValidKey(K key) {
        if (key == null)
            throw new IllegalArgumentException("Key cannot be null.");
    }

    /**
     * Implements getAll by delegating to get.
     * @throws SyncException
     */
    public static <K, V> Map<K, List<Versioned<V>>> getAll(IStore<K, V> storageEngine, Iterable<K> keys)
            throws SyncException {
        Map<K, List<Versioned<V>>> result = newEmptyHashMap(keys);
        for (K key : keys) {
            List<Versioned<V>> value = storageEngine.get(key);
            if (!value.isEmpty())
                result.put(key, value);
        }
        return result;
    }

    /**
     * Returns an empty map with expected size matching the iterable size if
     * it's of type Collection. Otherwise, an empty map with the default size is
     * returned.
     */
    public static <K, V> HashMap<K, V> newEmptyHashMap(Iterable<?> iterable) {
        if (iterable instanceof Collection<?>)
            return Maps.newHashMapWithExpectedSize(((Collection<?>) iterable).size());
        return Maps.newHashMap();
    }

    /**
     * Closes a Closeable and logs a potential error instead of re-throwing the
     * exception. If {@code null} is passed, this method is a no-op.
     *
     * This is typically used in finally blocks to prevent an exception thrown
     * during close from hiding an exception thrown inside the try.
     *
     * @param c The Closeable to close, may be null.
     */
    public static void close(Closeable c) {
        if (c != null) {
            try {
                c.close();
            } catch (IOException e) {
                logger.error("Error closing stream", e);
            }
        }
    }

    public static <V> List<IVersion> getVersions(List<Versioned<V>> versioneds) {
        List<IVersion> versions = Lists.newArrayListWithCapacity(versioneds.size());
        for (Versioned<?> versioned : versioneds)
            versions.add(versioned.getVersion());
        return versions;
    }

    public static <K, V> IClosableIterator<K> keys(final IClosableIterator<Entry<K, V>> values) {
        return new IClosableIterator<K>() {

            public void close() {
                values.close();
            }

            public boolean hasNext() {
                return values.hasNext();
            }

            public K next() {
                Entry<K, V> value = values.next();
                if (value == null)
                    return null;
                return value.getKey();
            }

            public void remove() {
                values.remove();
            }

        };
    }

    public static <V> boolean canDelete(List<Versioned<V>> items, long tombstoneDeletion) {
        List<VectorClock> tombstones = new ArrayList<VectorClock>();
        long now = System.currentTimeMillis();
        // make two passes; first we find tombstones that are old enough.
        for (Versioned<V> v : items) {
            if (v.getValue() == null) {
                VectorClock vc = (VectorClock) v.getVersion();
                if ((vc.getTimestamp() + tombstoneDeletion) < now)
                    tombstones.add(vc);
            }
        }

        // second, if we find a tombstone which is later than every
        // non-tombstone value, then we can delete the key.
        for (VectorClock vc : tombstones) {
            boolean later = true;
            for (Versioned<V> v : items) {
                if (v.getValue() != null) {
                    VectorClock curvc = (VectorClock) v.getVersion();
                    if (!Occurred.AFTER.equals(vc.compare(curvc))) {
                        later = false;
                        break;
                    }
                }
            }
            if (later) {
                // we found a tombstone that's old enough and 
                // logically later than all non-tombstones.  We can 
                // remove the value from the map.
                return true;
            }
        }

        return false;
    }
}