/* Copyright (c) 2012-2014 Boundless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/edl-v10.html
*
* Contributors:
* Gabriel Roldan (Boundless) - initial implementation
*/
package org.locationtech.geogig.storage.memory;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.RevObject;
import org.locationtech.geogig.storage.AbstractObjectDatabase;
import org.locationtech.geogig.storage.BulkOpListener;
import org.locationtech.geogig.storage.ObjectDatabase;
import org.locationtech.geogig.storage.datastream.DataStreamSerializationFactoryV1;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ning.compress.lzf.LZFInputStream;
/**
* Provides an implementation of a GeoGig object database that utilizes the heap for the storage of
* objects.
*
* @see AbstractObjectDatabase
*/
public class HeapObjectDatabse extends AbstractObjectDatabase implements ObjectDatabase {
private ConcurrentMap<ObjectId, byte[]> objects;
public HeapObjectDatabse() {
super(DataStreamSerializationFactoryV1.INSTANCE);
}
/**
* Closes the database.
*
* @see org.locationtech.geogig.storage.ObjectDatabase#close()
*/
@Override
public void close() {
if (objects != null) {
objects.clear();
objects = null;
}
}
/**
* @return true if the database is open, false otherwise
*/
@Override
public boolean isOpen() {
return objects != null;
}
/**
* Opens the database for use by GeoGig.
*/
@Override
public void open() {
if (isOpen()) {
return;
}
objects = Maps.newConcurrentMap();
}
/**
* Determines if the given {@link ObjectId} exists in the object database.
*
* @param id the id to search for
* @return true if the object exists, false otherwise
*/
@Override
public boolean exists(ObjectId id) {
checkNotNull(id);
return objects.containsKey(id);
}
/**
* Deletes the object with the provided {@link ObjectId id} from the database.
*
* @param objectId the id of the object to delete
* @return true if the object was deleted, false if it was not found
*/
@Override
public boolean delete(ObjectId objectId) {
checkNotNull(objectId);
return objects.remove(objectId) != null;
}
@Override
protected List<ObjectId> lookUpInternal(byte[] raw) {
throw new UnsupportedOperationException("we override lookup directly");
}
/**
* Searches the database for {@link ObjectId}s that match the given partial id.
*
* @param partialId the partial id to search for
* @return a list of matching results
*/
@Override
public List<ObjectId> lookUp(final String partialId) {
Preconditions.checkNotNull(partialId);
List<ObjectId> matches = Lists.newLinkedList();
for (ObjectId id : objects.keySet()) {
if (id.toString().startsWith(partialId)) {
matches.add(id);
}
}
return matches;
}
@Override
protected InputStream getRawInternal(ObjectId id, boolean failIfNotFound)
throws IllegalArgumentException {
byte[] data = objects.get(id);
if (data == null) {
if (failIfNotFound) {
throw new IllegalArgumentException(id + " does not exist");
}
return null;
}
return new ByteArrayInputStream(data);
}
@Override
protected boolean putInternal(ObjectId id, byte[] rawData) {
byte[] previousValue = objects.putIfAbsent(id, rawData);
return previousValue == null;
}
@Override
public long deleteAll(Iterator<ObjectId> ids, final BulkOpListener listener) {
long count = 0;
while (ids.hasNext()) {
ObjectId id = ids.next();
byte[] removed = this.objects.remove(id);
if (removed != null) {
count++;
listener.deleted(id);
} else {
listener.notFound(id);
}
}
return count;
}
@Override
public Iterator<RevObject> getAll(final Iterable<ObjectId> ids, final BulkOpListener listener) {
return new AbstractIterator<RevObject>() {
final Iterator<ObjectId> iterator = ids.iterator();
@Override
protected RevObject computeNext() {
RevObject found = null;
ObjectId id;
byte[] raw;
while (iterator.hasNext() && found == null) {
id = iterator.next();
raw = objects.get(id);
if (raw != null) {
try {
found = serializationFactory.createObjectReader().read(id,
new LZFInputStream(new ByteArrayInputStream(raw)));
} catch (IOException e) {
throw Throwables.propagate(e);
}
listener.found(found.getId(), raw.length);
} else {
listener.notFound(id);
}
}
return found == null ? endOfData() : found;
}
};
}
@Override
public void configure() {
// No-op
}
@Override
public void checkConfig() {
// No-op
}
@Override
public String toString() {
return getClass().getSimpleName();
}
}