/**
* OpenSpotLight - Open Source IT Governance Platform
*
* Copyright (c) 2009, CARAVELATECH CONSULTORIA E TECNOLOGIA EM INFORMATICA LTDA
* or third-party contributors as indicated by the @author tags or express
* copyright attribution statements applied by the authors. All third-party
* contributions are distributed under license by CARAVELATECH CONSULTORIA E
* TECNOLOGIA EM INFORMATICA LTDA.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
***********************************************************************
* OpenSpotLight - Plataforma de Governança de TI de Código Aberto
*
* Direitos Autorais Reservados (c) 2009, CARAVELATECH CONSULTORIA E TECNOLOGIA
* EM INFORMATICA LTDA ou como contribuidores terceiros indicados pela etiqueta
* @author ou por expressa atribuição de direito autoral declarada e atribuída pelo autor.
* Todas as contribuições de terceiros estão distribuídas sob licença da
* CARAVELATECH CONSULTORIA E TECNOLOGIA EM INFORMATICA LTDA.
*
* Este programa é software livre; você pode redistribuí-lo e/ou modificá-lo sob os
* termos da Licença Pública Geral Menor do GNU conforme publicada pela Free Software
* Foundation.
*
* Este programa é distribuído na expectativa de que seja útil, porém, SEM NENHUMA
* GARANTIA; nem mesmo a garantia implícita de COMERCIABILIDADE OU ADEQUAÇÃO A UMA
* FINALIDADE ESPECÍFICA. Consulte a Licença Pública Geral Menor do GNU para mais detalhes.
*
* Você deve ter recebido uma cópia da Licença Pública Geral Menor do GNU junto com este
* programa; se não, escreva para:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.openspotlight.storage.mongodb;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.newLinkedList;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Sets.newHashSet;
import static java.util.Collections.emptySet;
import static org.openspotlight.common.Pair.newPair;
import static org.openspotlight.common.util.Assertions.checkNotEmpty;
import static org.openspotlight.common.util.Assertions.checkNotNull;
import static org.openspotlight.common.util.Conversion.convert;
import static org.openspotlight.storage.StringKeysSupport.getNodeType;
import static org.openspotlight.storage.StringKeysSupport.getPartition;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;
import org.openspotlight.common.Pair;
import org.openspotlight.common.collection.IteratorBuilder;
import org.openspotlight.common.collection.IteratorBuilder.Converter;
import org.openspotlight.common.collection.IteratorBuilder.SimpleIteratorBuilder;
import org.openspotlight.common.util.SLCollections;
import org.openspotlight.storage.NodeCriteria;
import org.openspotlight.storage.NodeCriteria.NodeCriteriaItem;
import org.openspotlight.storage.NodeCriteria.NodeCriteriaItem.CompositeKeyCriteriaItem;
import org.openspotlight.storage.NodeCriteria.NodeCriteriaItem.NodeKeyAsStringCriteriaItem;
import org.openspotlight.storage.NodeCriteria.NodeCriteriaItem.NodeKeyCriteriaItem;
import org.openspotlight.storage.NodeCriteria.NodeCriteriaItem.PropertyContainsString;
import org.openspotlight.storage.NodeCriteria.NodeCriteriaItem.PropertyCriteriaItem;
import org.openspotlight.storage.NodeCriteria.NodeCriteriaItem.PropertyEndsWithString;
import org.openspotlight.storage.NodeCriteria.NodeCriteriaItem.PropertyStartsWithString;
import org.openspotlight.storage.NodeKeyBuilder;
import org.openspotlight.storage.NodeKeyBuilderImpl;
import org.openspotlight.storage.Partition;
import org.openspotlight.storage.PartitionFactory;
import org.openspotlight.storage.StorageSession.FlushMode;
import org.openspotlight.storage.StringKeysSupport;
import org.openspotlight.storage.domain.Property;
import org.openspotlight.storage.domain.PropertyContainer;
import org.openspotlight.storage.domain.PropertyImpl;
import org.openspotlight.storage.domain.StorageLink;
import org.openspotlight.storage.domain.StorageLinkImpl;
import org.openspotlight.storage.domain.StorageNode;
import org.openspotlight.storage.domain.StorageNodeImpl;
import org.openspotlight.storage.domain.key.NodeKey;
import org.openspotlight.storage.domain.key.NodeKey.CompositeKey.SimpleKey;
import org.openspotlight.storage.engine.StorageEngineBind;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.inject.Inject;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.gridfs.GridFSInputFile;
/**
* Created by User: feu - Date: Mar 23, 2010 - Time: 4:46:25 PM
*/
public class MongoStorageSessionImpl implements StorageEngineBind<DBObject, DBObject> {
private static final String _ = "_";
private static final String ID = "_id";
private static final String LOCAL_ID = "node_local_id";
private static final String PARENT_ID = "node_parent_id";
private static final String KEY_NAMES = "node_key_names";
private static final String PROPERTIES = "node_properties";
private static final String INDEXED = "node_indexed";
private static final String LINKS = "links";
private static final String NODE_TYPE = "node_type";
private static final String NULL_VALUE = "!!!NULL!!!";
private final FlushMode flushMode;
private final int maxCacheSize;
private final Mongo mongo;
private final Map<Pair<String, String>, DBCollection> collectionsMap = newHashMap();
private final Map<String, GridFS> gridFSMap;
private final LinkedList<Pair<NodeKey, DBObject>> objectCache = newLinkedList();
private final PartitionFactory partitionFactory;
private final Map<String, DB> partitionMap;
private final Multimap<Partition, Pair<StorageNode, DBObject>> transientObjects = HashMultimap
.create();
Set<String> allIndexes = newHashSet();
@Inject
public MongoStorageSessionImpl(final Mongo mongo, final FlushMode flushMode, final PartitionFactory partitionFactory,
final int maxCacheSize) {
this.partitionFactory = partitionFactory;
this.maxCacheSize = maxCacheSize;
partitionMap = newHashMap();
this.mongo = mongo;
gridFSMap = newHashMap();
this.flushMode = flushMode;
}
private static String beforeRegex(final String s) {
return s;
}
private StorageNode convertToNode(final Partition partition, final DBObject dbObject)
throws Exception {
final DBObject keyAsDbObj = (DBObject) dbObject.get(INDEXED);
final List<String> keyNames = (List<String>) dbObject.get(KEY_NAMES);
NodeKeyBuilder keyBuilder = new NodeKeyBuilderImpl((String) dbObject.get(NODE_TYPE), partition);
for (final String s: keyAsDbObj.keySet()) {
if (keyNames.contains(s)) {
String valueAsString = convert(keyAsDbObj.get(s), String.class);
if (NULL_VALUE.equals(valueAsString)) {
valueAsString = null;
}
keyBuilder.withSimpleKey(s, valueAsString);
}
}
final String parentId = (String) dbObject.get(PARENT_ID);
if (parentId != null) {
keyBuilder.withParent(parentId);
}
final NodeKey uniqueKey = keyBuilder.andCreate();
final StorageNode node = new StorageNodeImpl(uniqueKey, false);
return node;
}
private void ensureIndexed(final Partition partition, final String parentName, final String groupName,
final String propertyName, final StorageLink possibleParentAsLink) {
final String key = partition.getPartitionName() + parentName + groupName + propertyName;
if (!allIndexes.contains(key)) {
allIndexes.add(key);
getCachedCollection(partition, parentName).ensureIndex(
groupName != null ? (groupName + "." + propertyName) : propertyName);
}
}
private DBObject findReferenceOrReturnNull(final PropertyContainer element) {
DBObject basicDBObject = null;
StorageNode node;
if (element instanceof StorageNode) {
node = (StorageNode) element;
} else if (element instanceof StorageLink) {
node = ((StorageLink) element).getSource();
} else {
throw new IllegalStateException();
}
final Pair<StorageNode, DBObject> p = Pair.<StorageNode, DBObject>newPair(node, null, Pair.PairEqualsMode.K1);
if (transientObjects.get(element.getPartition()).contains(p)) {
for (final Pair<StorageNode, DBObject> pair: transientObjects.get(element.getPartition())) {
if (pair.equals(p)) {
basicDBObject = pair.getK2();
break;
}
}
}
NodeKey key;
String collectionName;
key = node.getKey();
collectionName = node.getType();
final Pair<NodeKey, DBObject> p1 = newPair(key, null, Pair.PairEqualsMode.K1);
final int idx = objectCache.indexOf(p1);
if (idx != -1) {
basicDBObject = objectCache.get(idx).getK2();
}
if (basicDBObject == null) {
final DBCollection coll = getCachedCollection(element.getPartition(), collectionName);
final BasicDBObject queryObject = new BasicDBObject();
queryObject.put(ID, key.getKeyAsString());
basicDBObject = coll.findOne(queryObject);
if (basicDBObject == null) {
basicDBObject = new BasicDBObject();
basicDBObject.put(ID, key.getKeyAsString());
}
objectCache.addFirst(newPair(key, basicDBObject, Pair.PairEqualsMode.K1));
if (objectCache.size() > maxCacheSize) {
objectCache.removeLast();
}
}
return basicDBObject;
}
private String getBigPropertyName(final Property dirtyProperty) {
return "big_" + dirtyProperty.getPropertyName();
}
private DBCollection getCachedCollection(final Partition partition, final String collectionName) {
return getCachedCollection(partition.getPartitionName(), collectionName);
}
private DBCollection getCachedCollection(final String partition, final String collectionName) {
final Pair<String, String> key = newPair(partition, collectionName);
DBCollection collection = collectionsMap.get(key);
if (collection == null) {
final DB db = getCachedDbForPartition(partition);
collection = db.getCollection(collectionName);
collectionsMap.put(key, collection);
}
return collection;
}
private DB getCachedDbForPartition(final Partition partition) {
return getCachedDbForPartition(partition.getPartitionName());
}
private DB getCachedDbForPartition(final String partitionName) {
DB db = partitionMap.get(partitionName);
if (db == null) {
db = mongo.getDB(partitionName);
partitionMap.put(partitionName, db);
}
return db;
}
private GridFS getCachedGridFSForPartition(final Partition partition) {
return getCachedGridFSForPartition(partition.getPartitionName());
}
private GridFS getCachedGridFSForPartition(final String partition) {
GridFS fs = gridFSMap.get(partition);
if (fs == null) {
final DB db = getCachedDbForPartition(partition);
fs = new GridFS(db);
gridFSMap.put(partition, fs);
}
return fs;
}
private String getFileName(final Partition partition, final Property dirtyProperty) {
StorageNode nodeEntry;
if (dirtyProperty.getParent() instanceof StorageNode) {
nodeEntry = (StorageNode) dirtyProperty.getParent();
} else if (dirtyProperty.getParent() instanceof StorageLink) {
nodeEntry = ((StorageLink) dirtyProperty.getParent()).getSource();
} else {
throw new IllegalStateException();
}
return partition.getPartitionName() + _ + nodeEntry.getKey().getKeyAsString() + _ + dirtyProperty.getPropertyName();
}
private Iterable<StorageNode> internalGetChildren(final Partition partition, final StorageNode node, final String type)
throws Exception {
final BasicDBObject baseDbObj = new BasicDBObject();
baseDbObj.put(PARENT_ID, node.getKey().getKeyAsString());
final ImmutableSet.Builder<String> names = ImmutableSet.builder();
if (type != null) {
names.add(type);
} else {
names.addAll(getCachedDbForPartition(partition).getCollectionNames());
}
final List<Iterable<DBObject>> dbCursors = newLinkedList();
for (final String s: names.build()) {
final DBCursor resultAsDbObject = getCachedCollection(partition, s).find(baseDbObj);
dbCursors.add(resultAsDbObject);
}
final IteratorBuilder.SimpleIteratorBuilder<StorageNode, DBObject> b = IteratorBuilder.createIteratorBuilder();
b.withConverter(new IteratorBuilder.Converter<StorageNode, DBObject>() {
@Override
public StorageNode convert(final DBObject nodeEntry)
throws Exception {
return convertToNode(partition, nodeEntry);
}
});
return b.withItems(SLCollections.<DBObject>iterableOfAll(dbCursors)).andBuild();
}
@Override
public DBObject createLinkReference(final StorageLink link)
throws IllegalStateException {
checkNotNull("link", link);
final StorageNode source = link.getSource();
final DBObject nodeRef = createNodeReference(source);
DBObject linkRef = null;
@SuppressWarnings("unchecked")
List<DBObject> links = (List<DBObject>) nodeRef.get(LINKS);
if (links == null) {
links = new ArrayList<DBObject>();
linkRef = new BasicDBObject();
links.add(linkRef);
linkRef.put(ID, link.getKeyAsString());
nodeRef.put(LINKS, links);
} else {
for (final DBObject possibleLink: links) {
if (possibleLink.get(ID).equals(link.getKeyAsString())) {
linkRef = possibleLink;
break;
}
}
if (linkRef == null) {
linkRef = new BasicDBObject();
links.add(linkRef);
linkRef.put(ID, link.getKeyAsString());
}
}
nodeRef.put(LINKS, links);
return linkRef;
}
@Override
public DBObject createNodeReference(final StorageNode node)
throws IllegalStateException {
checkNotNull("node", node);
final DBObject basicDBObject = findReferenceOrReturnNull(node);
final Pair<StorageNode, DBObject> p = Pair.<StorageNode, DBObject>newPair(node, basicDBObject, Pair.PairEqualsMode.K1);
if (!transientObjects.get(node.getPartition()).contains(p)) {
transientObjects.put(node.getPartition(), p);
}
return basicDBObject;
}
@Override
public void persistNode(final DBObject reference, final StorageNode node)
throws Exception, IllegalStateException {
checkNotNull("reference", reference);
checkNotNull("node", node);
reference.put(LOCAL_ID, node.getKey().getCompositeKey().getKeyAsString());
ensureIndexed(node.getPartition(), node.getType(), null, LOCAL_ID, null);
final NodeKey uniqueId = node.getKey();
final String parentId = uniqueId.getParentKeyAsString();
if (parentId != null) {
reference.put(PARENT_ID, parentId);
}
final BasicDBObject key = new BasicDBObject();
final List<String> keyNames = newArrayList();
for (final SimpleKey keyEntry: uniqueId.getCompositeKey().getKeys()) {
keyNames.add(keyEntry.getKeyName());
key.put(keyEntry.getKeyName(), keyEntry.getValue() != null ? keyEntry.getValue() : NULL_VALUE);
ensureIndexed(node.getPartition(), node.getType(), INDEXED, keyEntry.getKeyName(), null);
}
reference.put(ID, uniqueId.getKeyAsString());
reference.put(KEY_NAMES, keyNames);
reference.put(INDEXED, key);
reference.put(NODE_TYPE, uniqueId.getCompositeKey().getNodeType());
if (FlushMode.AUTO.equals(flushMode)) {
final DBCollection col = getCachedCollection(node.getPartition(), node.getType());
col.save(reference);
} else {
final Pair<StorageNode, DBObject> p = Pair.<StorageNode, DBObject>newPair(node, reference, Pair.PairEqualsMode.K1);
if (!transientObjects.get(node.getPartition()).contains(p)) {
transientObjects.put(node.getPartition(), p);
}
}
}
@Override
public void deleteNode(final StorageNode node)
throws Exception, IllegalArgumentException {
checkNotNull("node", node);
final DBCollection collection = getCachedCollection(node.getPartition(), node.getType());
collection.remove(new BasicDBObject(ID, node.getKey().getKeyAsString()));
}
@Override
public void deleteLink(final StorageLink link)
throws Exception, IllegalStateException {
checkNotNull("link", link);
final DBObject basicDBObject = findReferenceOrReturnNull(link.getSource());
if (basicDBObject != null) {
@SuppressWarnings("unchecked")
final List<DBObject> links = (List<DBObject>) basicDBObject.get(LINKS);
if (links != null) {
for (final DBObject possibleLink: links) {
if (possibleLink.get(ID).equals(link.getKeyAsString())) {
links.remove(possibleLink);
break;
}
}
}
}
}
@Override
public void persistLink(final StorageLink link)
throws Exception, IllegalStateException {
checkNotNull("link", link);
createLinkReference(link);
if (flushMode.equals(FlushMode.AUTO)) {
final DBObject nodeRef = createNodeReference(link.getSource());
final DBCollection col = getCachedCollection(link.getSource().getPartition(), link.getSource().getType());
col.save(nodeRef);
}
}
@Override
public Iterable<StorageNode> search(final NodeCriteria criteria)
throws Exception, IllegalStateException {
checkNotNull("criteria", criteria);
final DBObject criteriaAsObj = new BasicDBObject();
for (final NodeCriteriaItem c: criteria.getCriteriaItems()) {
if (c instanceof PropertyCriteriaItem) {
final PropertyCriteriaItem p = (PropertyCriteriaItem) c;
criteriaAsObj.put(INDEXED + "." + p.getPropertyName(), p.getValue() == null ? NULL_VALUE : p.getValue());
}
if (c instanceof PropertyContainsString) {
final PropertyContainsString p = (PropertyContainsString) c;
criteriaAsObj.put(INDEXED + "." + p.getPropertyName(),
Pattern.compile("(.*)" + beforeRegex(p.getValue()) + "(.*)"));
}
if (c instanceof PropertyStartsWithString) {
final PropertyStartsWithString p = (PropertyStartsWithString) c;
criteriaAsObj.put(INDEXED + "." + p.getPropertyName(), Pattern.compile("^" + beforeRegex(p.getValue()) + "(.*)"));
}
if (c instanceof PropertyEndsWithString) {
final PropertyEndsWithString p = (PropertyEndsWithString) c;
criteriaAsObj.put(INDEXED + "." + p.getPropertyName(), Pattern.compile("(.*)" + beforeRegex(p.getValue()) + "$"));
}
if (c instanceof NodeKeyCriteriaItem) {
final NodeKeyCriteriaItem uniqueCriteria = (NodeKeyCriteriaItem) c;
criteriaAsObj.put(ID, uniqueCriteria.getValue().getKeyAsString());
}
if (c instanceof NodeKeyAsStringCriteriaItem) {
final NodeKeyAsStringCriteriaItem uniqueCriteria = (NodeKeyAsStringCriteriaItem) c;
criteriaAsObj.put(ID, uniqueCriteria.getKeyAsString());
}
if (c instanceof CompositeKeyCriteriaItem) {
final CompositeKeyCriteriaItem uniqueCriteria = (CompositeKeyCriteriaItem) c;
final String localHash = uniqueCriteria.getValue().getKeyAsString();
criteriaAsObj.put(LOCAL_ID, localHash);
}
}
final ImmutableSet.Builder<String> nodeNamesBuilder = ImmutableSet.builder();
if (criteria.getNodeType() != null) {
nodeNamesBuilder.add(criteria.getNodeType());
} else {
nodeNamesBuilder.addAll(getCachedDbForPartition(criteria.getPartition()).getCollectionNames());
}
final List<Iterable<DBObject>> dbCursors = newLinkedList();
for (final String s: nodeNamesBuilder.build()) {
final DBCursor resultAsDbObject = getCachedCollection(criteria.getPartition(), s).find(criteriaAsObj);
dbCursors.add(resultAsDbObject);
}
final IteratorBuilder.SimpleIteratorBuilder<StorageNode, DBObject> b = IteratorBuilder.createIteratorBuilder();
b.withConverter(new IteratorBuilder.Converter<StorageNode, DBObject>() {
@Override
public StorageNode convert(final DBObject nodeEntry)
throws Exception {
return convertToNode(criteria.getPartition(), nodeEntry);
}
});
return b.withItems(SLCollections.<DBObject>iterableOfAll(dbCursors)).andBuild();
}
@Override
public Iterable<StorageNode> getNodes(final Partition partition, final String type)
throws Exception, IllegalStateException {
checkNotNull("partition", partition);
checkNotEmpty("type", type);
final DBCursor cursor = getCachedCollection(partition, type).find();
final ImmutableSet.Builder<StorageNode> builder = ImmutableSet
.builder();
while (cursor.hasNext()) {
builder.add(convertToNode(partition, cursor.next()));
}
return builder.build();
}
@Override
public Iterable<StorageLink> getLinks(final StorageNode source, final StorageNode target, final String type)
throws Exception, IllegalStateException {
checkNotNull("source", source);
final Builder<String> rawItems = ImmutableList.builder();
final DBObject basicDBObject = findReferenceOrReturnNull(source);
if (basicDBObject != null) {
@SuppressWarnings("unchecked")
final List<DBObject> links = (List<DBObject>) basicDBObject.get(LINKS);
if (links != null) {
for (final DBObject possibleLink: links) {
final String linkId = (String) possibleLink.get(ID);
if (type != null && target != null) {
if (StringKeysSupport.getLinkTypeFromLinkKey(linkId).equals(type) &&
StringKeysSupport.getTargeyKeyAsStringFromLinkKey(linkId).equals(target.getKeyAsString())) {
rawItems.add(linkId);
}
} else if (type != null) {
if (StringKeysSupport.getLinkTypeFromLinkKey(linkId).equals(type)) {
rawItems.add(linkId);
}
} else if (target != null) {
if (StringKeysSupport.getTargeyKeyAsStringFromLinkKey(linkId).equals(target.getKeyAsString())) {
rawItems.add(linkId);
}
} else {
rawItems.add(linkId);
}
}
}
}
@SuppressWarnings("unchecked")
final SimpleIteratorBuilder<StorageLink, String> result = IteratorBuilder
.<StorageLink, String>createIteratorBuilder()
.withItems(rawItems.build())
.withConverter(new Converter<StorageLink, String>() {
@Override
public StorageLink convert(final String o)
throws Exception {
StorageNode foundTarget = target;
if (foundTarget == null) {
final String targetId = StringKeysSupport.getTargeyKeyAsStringFromLinkKey(o);
final Partition targetPartition =
partitionFactory.getPartition(StringKeysSupport.getPartitionName(targetId));
foundTarget = internalGetNode(targetId, targetPartition);
if (foundTarget == null) {
throw new IllegalStateException();
}
}
final String foundName = StringKeysSupport.getLinkTypeFromLinkKey(o);
return new StorageLinkImpl(foundName, source, foundTarget, true);
}
});
return result.andBuild();
}
@Override
public void setNodeProperty(DBObject reference, Property property)
throws Exception, IllegalStateException {
flushSimpleProperty(reference, property);
}
@Override
public void setLinkProperty(DBObject reference, Property property)
throws Exception, IllegalStateException {
flushSimpleProperty(reference, property);
}
private void flushSimpleProperty(final DBObject ref, final Property property)
throws Exception {
checkNotNull("property", property);
DBObject reference;
String collectionName;
if (ref != null) {
reference = ref;
collectionName = StringKeysSupport.getNodeType((String) reference.get(ID));
} else if (property.getParent() instanceof StorageNode) {
reference = createNodeReference((StorageNode) property.getParent());
collectionName = ((StorageNode) property.getParent()).getType();
} else if (property.getParent() instanceof StorageLink) {
reference = createLinkReference((StorageLink) property.getParent());
collectionName = ((StorageLink) property.getParent()).getSource().getType();
} else {
throw new IllegalStateException();
}
String objName = null;
Object value = null;
if (property.isIndexed()) {
ensureIndexed(property.getParent().getPartition(), collectionName, INDEXED, property.getPropertyName(),
null);
objName = INDEXED;
value = ((PropertyImpl) property).getTransientValueAsString();
if (value == null) {
value = NULL_VALUE;
}
} else if (!property.isKey()) {
objName = PROPERTIES;
value = ((PropertyImpl) property).getTransientValueAsBytes();
}
if (objName == null) { return; }
DBObject obj = (DBObject) reference.get(objName);
if (obj == null) {
obj = new BasicDBObject();
reference.put(objName, obj);
}
if (value instanceof byte[] && isBiggerThan4mb((byte[]) value)) {
obj.put(getBigPropertyName(property), true);
} else {
obj.removeField(getBigPropertyName(property));
obj.put(property.getPropertyName(), value);
StorageNode nodeEntry;
if (property.getParent() instanceof StorageNode) {
nodeEntry = (StorageNode) property.getParent();
} else if (property.getParent() instanceof StorageLink) {
nodeEntry = ((StorageLink) property.getParent()).getSource();
} else {
throw new IllegalStateException();
}
if (FlushMode.AUTO.equals(flushMode)) {
getCachedCollection(property.getParent().getPartition(), nodeEntry.getType()).save(reference);
} else {
final Pair<StorageNode, DBObject> p = newPair(nodeEntry, reference, Pair.PairEqualsMode.K1);
if (!transientObjects.get(property.getParent().getPartition()).contains(p)) {
transientObjects.put(property.getParent().getPartition(), p);
}
}
}
}
@Override
public Iterable<String> getAllNodeTypes(final Partition partition)
throws Exception, IllegalStateException {
checkNotNull("partition", partition);
final HashSet<String> set = new HashSet<String>();
set.addAll(getCachedDbForPartition(partition).getCollectionNames());
set.remove("system.indexes");
return ImmutableSet.copyOf(set);
}
@Override
public Iterable<StorageNode> getChildren(final Partition partition, final StorageNode node)
throws Exception, IllegalStateException {
checkNotNull("partition", partition);
checkNotNull("node", node);
return internalGetChildren(partition, node, null);
}
@Override
public StorageNode getNode(String key)
throws Exception, IllegalStateException {
checkNotEmpty("key", key);
return internalGetNode(key, getPartition(key, partitionFactory));
}
public StorageNode internalGetNode(String key, Partition partition)
throws Exception {
final String nodeType = getNodeType(key);
final BasicDBObject parameter = new BasicDBObject();
parameter.put(ID, key);
final DBCollection collection = getCachedCollection(partition, nodeType);
final DBObject result = collection.findOne(parameter);
return convertToNode(partition, result);
}
@Override
public Iterable<StorageNode> getChildren(final Partition partition, final StorageNode node, final String type)
throws Exception, IllegalStateException {
checkNotNull("partition", partition);
checkNotNull("node", node);
checkNotEmpty("type", type);
return internalGetChildren(partition, node, type);
}
@Override
public StorageNode getParent(final StorageNode node)
throws Exception, IllegalStateException {
checkNotNull("node", node);
final String parentKey = node.getKey().getParentKeyAsString();
if (parentKey == null) { return null; }
return getNode(parentKey);
}
@Override
public Set<Property> getProperties(final PropertyContainer element)
throws Exception, IllegalStateException {
checkNotNull("element", element);
if (element instanceof StorageNode) {
final StorageNode node = (StorageNode) element;
final ImmutableSet.Builder<Property> builder = ImmutableSet.builder();
for (final SimpleKey entry: node.getKey().getCompositeKey().getKeys()) {
final PropertyImpl p = PropertyImpl.createKey(entry.getKeyName(), element);
(p).setStringValueOnLoad(entry.getValue());
builder.add(p);
}
final DBObject reference = createNodeReference(node);
final DBObject indexed = (DBObject) reference.get(INDEXED);
final List<String> keyNames = (List<String>) reference.get(KEY_NAMES);
if (indexed != null) {
for (final String s: indexed.keySet()) {
if (!keyNames.contains(s)) {
final PropertyImpl p = PropertyImpl.createIndexed(s, element);
String value = (String) indexed.get(s);
if (NULL_VALUE.equals(value)) {
value = null;
}
(p).setStringValueOnLoad(value);
builder.add(p);
}
}
}
final DBObject properties = (DBObject) reference.get(PROPERTIES);
if (properties != null) {
for (final String s: properties.keySet()) {
final PropertyImpl p = PropertyImpl.createSimple(s,
element);
builder.add(p);
}
}
return builder.build();
} else if (element instanceof StorageLink) {
final StorageLink linkEntry = (StorageLink) element;
final ImmutableSet.Builder<Property> builder = ImmutableSet.builder();
final DBObject reference = createLinkReference(linkEntry);
final DBObject indexed = (DBObject) reference.get(INDEXED);
if (indexed != null) {
for (final String s: indexed.keySet()) {
final PropertyImpl p = PropertyImpl.createIndexed(s, element);
String value = (String) indexed.get(s);
if (NULL_VALUE.equals(value)) {
value = null;
}
(p).setStringValueOnLoad(value);
builder.add(p);
}
}
final DBObject properties = (DBObject) reference.get(PROPERTIES);
if (properties != null) {
for (final String s: properties.keySet()) {
final PropertyImpl p = PropertyImpl.createSimple(s, element);
builder.add(p);
}
}
return builder.build();
} else {
throw new IllegalStateException();
}
}
@Override
public byte[] getPropertyValue(final Property property)
throws Exception, IllegalStateException {
checkNotNull("property", property);
byte[] value = null;
if (property.isKey()) {
final StorageNode parent = (StorageNode) property.getParent();
for (final SimpleKey e: parent.getKey().getCompositeKey().getKeys()) {
if (e.getKeyName().equals(property.getPropertyName())) {
value = e.getValue() != null ? e.getValue().getBytes() : null;
if (NULL_VALUE.equals(new String(value))) {
value = null;
}
break;
}
}
} else {
final DBObject reference = findReferenceOrReturnNull(property.getParent());
if (reference != null) {
if (property.isIndexed()) {
final DBObject innerObj = (DBObject) reference.get(INDEXED);
if (innerObj != null) {
value = ((String) innerObj.get(property.getPropertyName())).getBytes();
if (NULL_VALUE.equals(new String(value))) {
value = null;
}
}
} else {
final DBObject innerObj = (DBObject) reference.get(PROPERTIES);
if (innerObj != null) {
final Boolean isBig = (Boolean) innerObj.get(getBigPropertyName(property));
if (Boolean.TRUE.equals(isBig)) {
value = readAsGridFS(property.getParent().getPartition(), property);
} else {
value = (byte[]) innerObj.get(property.getPropertyName());
}
if (NULL_VALUE.equals(new String(value))) {
value = null;
}
}
}
}
}
return value;
}
@Override
public void save(final Partition... partitions)
throws Exception {
for (final Partition partition: partitions) {
for (final Pair<StorageNode, DBObject> p: transientObjects.get(partition)) {
final StorageNode n = p.getK1();
final DBCollection coll = getCachedCollection(partition, n.getType());
coll.save(p.getK2());
}
}
transientObjects.clear();
}
boolean isBiggerThan4mb(final byte[] bytes) {
return (double) (bytes == null ? 0 : bytes.length) / (1024 * 1024) > 4.0;
}
@Override
public void closeResources() {
mongo.close();
}
public byte[] readAsGridFS(final Partition partition,
final Property property)
throws Exception {
final String key = getFileName(partition, property);
final GridFS fs = getCachedGridFSForPartition(partition);
final GridFSDBFile file = fs.findOne(key);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(file.getInputStream(), baos);
return baos.toByteArray();
}
// public void storeInGridFS(final Partition partition,
// final Property property, final byte[] value)
// throws Exception {
// final String key = getFileName(partition, property);
// final GridFS fs = getCachedGridFSForPartition(partition);
// final GridFSInputFile file = fs.createFile(value);
// file.setFilename(key);
// file.save();
// }
}