Package org.springframework.data.neo4j.support

Source Code of org.springframework.data.neo4j.support.DelegatingGraphDatabase

/**
* Copyright 2011 the original author or authors.
*
* 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.springframework.data.neo4j.support;

import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.graphdb.index.RelationshipIndex;
import org.neo4j.graphdb.index.UniqueFactory;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.index.lucene.ValueContext;
import org.neo4j.kernel.GraphDatabaseAPI;
import org.neo4j.kernel.Traversal;
import org.neo4j.kernel.impl.transaction.SpringTransactionManager;
import org.neo4j.tooling.GlobalGraphOperations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.neo4j.conversion.DefaultConverter;
import org.springframework.data.neo4j.conversion.ResultConverter;
import org.springframework.data.neo4j.core.GraphDatabase;
import org.springframework.data.neo4j.support.index.IndexType;
import org.springframework.data.neo4j.support.index.NoSuchIndexException;
import org.springframework.data.neo4j.support.query.ConversionServiceQueryResultConverter;
import org.springframework.data.neo4j.support.query.CypherQueryEngine;
import org.springframework.data.neo4j.support.query.CypherQueryEngineImpl;
import org.springframework.data.neo4j.support.schema.SchemaIndexProvider;
import org.springframework.util.ObjectUtils;

import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.TransactionManager;
import java.util.*;

import static org.neo4j.helpers.collection.MapUtil.map;

/**
* @author mh
* @since 29.03.11
*/
public class DelegatingGraphDatabase implements GraphDatabase {

    private static final Logger log = LoggerFactory.getLogger(DelegatingGraphDatabase.class);
    private static final Label[] NO_LABELS = new Label[0];
    private SchemaIndexProvider schemaIndexProvider;

    protected GraphDatabaseService delegate;
    private ConversionService conversionService;
    private ResultConverter resultConverter;
    private volatile CypherQueryEngineImpl cypherQueryEngine;

    public DelegatingGraphDatabase(final GraphDatabaseService delegate) {
        this(delegate,null);
    }
    public DelegatingGraphDatabase(final GraphDatabaseService delegate, ResultConverter resultConverter) {
        this.delegate = delegate;
        this.resultConverter = resultConverter;
        this.schemaIndexProvider = new SchemaIndexProvider(this);
    }

    public void setConversionService(ConversionService conversionService) {
        this.conversionService = conversionService;
    }

    @Override
    public void setResultConverter(ResultConverter resultConverter) {
        this.resultConverter = resultConverter;
        if (cypherQueryEngine != null) this.cypherQueryEngine.setResultConverter(this.resultConverter);
    }

    @Override
    public Node getNodeById(long id) {
        return delegate.getNodeById(id);
    }

    @Override
    public Node createNode(Map<String, Object> props, Collection<String> labels) {
        return setProperties(delegate.createNode(toLabels(labels)), props);
    }

    private Label[] toLabels(Collection<String> labels) {
        if (labels==null || labels.isEmpty()) return NO_LABELS;
        Label[] labelArray = new Label[labels.size()];
        int i=0;
        for (String label : labels) {
            labelArray[i++]= DynamicLabel.label(label);
        }
        return labelArray;
    }

    private <T extends PropertyContainer> T setProperties(T primitive, Map<String, Object> properties) {
        assert primitive != null;
        if (properties==null || properties.isEmpty()) return primitive;
        for (Map.Entry<String, Object> prop : properties.entrySet()) {
            if (prop.getValue()==null) {
                primitive.removeProperty(prop.getKey());
            } else {
                primitive.setProperty(prop.getKey(), prop.getValue());
            }
        }
        return primitive;
    }

    private void removeFromIndexes(Node node) {
        final IndexManager indexManager = delegate.index();
        for (String indexName : indexManager.nodeIndexNames()) {
            Index<Node> nodeIndex = indexManager.forNodes(indexName);
            if (nodeIndex.isWriteable()) nodeIndex.remove(node);
        }
    }

    private void removeFromIndexes(Relationship relationship) {
        final IndexManager indexManager = delegate.index();
        for (String indexName : indexManager.relationshipIndexNames()) {
            RelationshipIndex relationshipIndex = indexManager.forRelationships(indexName);
            if (relationshipIndex.isWriteable()) relationshipIndex.remove(relationship);
        }
    }

    @Override
    public Relationship getRelationshipById(long id) {
        return delegate.getRelationshipById(id);
    }

    @Override
    public Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map<String, Object> properties) {
        return setProperties(startNode.createRelationshipTo(endNode,type), properties);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends PropertyContainer> Index<T> getIndex(String indexName) {
        IndexManager indexManager = delegate.index();
        if (indexManager.existsForNodes(indexName)) return (Index<T>) indexManager.forNodes(indexName);
        if (indexManager.existsForRelationships(indexName)) return (Index<T>) indexManager.forRelationships(indexName);
        throw new NoSuchIndexException(indexName);
    }

    // TODO handle existing indexes
    @SuppressWarnings("unchecked")
    @Override
    public <T extends PropertyContainer> Index<T> createIndex(Class<T> type, String indexName, IndexType indexType) {
        Transaction tx = delegate.beginTx();
        try {
            IndexManager indexManager = delegate.index();
            if (isNode(type)) {
                if (indexManager.existsForNodes(indexName))
                    return (Index<T>) checkAndGetExistingIndex(indexName, indexType, indexManager.forNodes(indexName));
                Index<Node> index = indexManager.forNodes(indexName, indexConfigFor(indexType));
                return (Index<T>) index;
            } else {
                if (indexManager.existsForRelationships(indexName))
                    return (Index<T>) checkAndGetExistingIndex(indexName, indexType, indexManager.forRelationships(indexName));
                return (Index<T>) indexManager.forRelationships(indexName, indexConfigFor(indexType));
            }
        } finally {
            tx.success();tx.close();
        }
    }

    public boolean isNode(Class<? extends PropertyContainer> type) {
        if (type.equals(Node.class)) return true;
        if (type.equals(Relationship.class)) return false;
        throw new IllegalArgumentException("Unknown Graph Primitive, neither Node nor Relationship"+type);
    }

    private <T extends PropertyContainer> Index<T> checkAndGetExistingIndex(final String indexName, IndexType indexType, final Index<T> index) {
        Map<String, String> existingConfig = delegate.index().getConfiguration(index);
        Map<String, String> config = indexConfigFor(indexType);
        if (configCheck(config, existingConfig, "provider") && configCheck(config, existingConfig, "type")) return index;
        throw new IllegalArgumentException("Setup for index "+indexName+" does not match. Existing: "+existingConfig+" required "+config);
     }

    private boolean configCheck(Map<String, String> config, Map<String, String> existingConfig, String setting) {
        return ObjectUtils.nullSafeEquals(config.get(setting), existingConfig.get(setting));
    }
    private Map<String, String> indexConfigFor(IndexType indexType) {
        return indexType.getConfig();
    }

    @Override
    public TraversalDescription traversalDescription() {
        return Traversal.description();
    }

    public CypherQueryEngine queryEngine() {
        return queryEngine(createResultConverter());
    }

    @SuppressWarnings("unchecked")
    private CypherQueryEngineImpl queryEngine(ResultConverter resultConverter, boolean reinit) {
        if (reinit || cypherQueryEngine==null)
            synchronized (this) {
                if (reinit || cypherQueryEngine==null) cypherQueryEngine = createCypherQueryEngine(resultConverter);
            }
        return cypherQueryEngine;
    }

    @SuppressWarnings("unchecked")
    public CypherQueryEngineImpl queryEngine(ResultConverter resultConverter) {
        return queryEngine(resultConverter, false);
    }

    private CypherQueryEngineImpl createCypherQueryEngine(ResultConverter resultConverter) {
        return new CypherQueryEngineImpl(delegate, resultConverter);
    }

    @Override
    public boolean transactionIsRunning() {
        if (!(delegate instanceof GraphDatabaseAPI)) {
            return true; // assume always running tx (e.g. for REST or other remotes)
        }
        try {
            final TransactionManager txManager = ((GraphDatabaseAPI) delegate).getDependencyResolver().resolveDependency(TransactionManager.class);
            return txManager.getStatus() != Status.STATUS_NO_TRANSACTION;
        } catch (SystemException e) {
            log.error("Error accessing TransactionManager", e);
            return false;
        }
    }

    @Override
    public TransactionManager getTransactionManager() {
        return new SpringTransactionManager((GraphDatabaseAPI)delegate);
    }

    @Override
    public Transaction beginTx() {
        return delegate.beginTx();
    }

    @Override
    public void remove(Node node) {
        removeFromIndexes(node);
        node.delete();
    }

    @Override
    public void remove(Relationship relationship) {
       removeFromIndexes(relationship);
       relationship.delete();
    }

    private ResultConverter createResultConverter() {
        if (resultConverter!=null) return resultConverter;
        if (conversionService != null) {
            this.resultConverter = new ConversionServiceQueryResultConverter(conversionService);
        } else {
            this.resultConverter = new DefaultConverter();
        }
        return resultConverter;
    }

    @Override
    public void shutdown() {
        delegate.shutdown();
    }

    @Override
    public Collection<String> getAllLabelNames() {
        Set<String> labels=new HashSet<>();
        for (Label label : GlobalGraphOperations.at(delegate).getAllLabels()) {
            labels.add(label.name());
        }
        return labels;
    }

    @Override
    public Relationship getOrCreateRelationship(Node start, Node end, RelationshipType type, Direction direction, Map<String, Object> props) {
        final Iterable<Relationship> existingRelationships = start.getRelationships(type, direction);
        for (final Relationship existingRelationship : existingRelationships) {
            if (existingRelationship != null && existingRelationship.getOtherNode(start).equals(end))
                return existingRelationship;
        }
        Relationship rel = direction == Direction.INCOMING ? end.createRelationshipTo(start, type) : start.createRelationshipTo(end, type);
        setProperties(rel, props);
        return rel;
    }

    public GraphDatabaseService getGraphDatabaseService() {
        return delegate;
    }

    public Node merge(String labelName, String key, Object value, final Map<String, Object> nodeProperties, Collection<String> labels) {
        return schemaIndexProvider.merge(labelName,key,value,nodeProperties,labels);
    }

    public Node getOrCreateNode(String indexName, String key, Object value, final Map<String, Object> nodeProperties, final Collection<String> labels) {
        if (indexName ==null || key == null || value==null) throw new IllegalArgumentException("Unique index "+ indexName +" key "+key+" value must not be null");
        if (value instanceof Number) value= ValueContext.numeric((Number)value);
        UniqueFactory.UniqueNodeFactory factory = new UniqueFactory.UniqueNodeFactory(delegate, indexName) {
            protected void initialize(Node node, Map<String, Object> _) {
                setProperties(node,nodeProperties);
                setLabels(node,labels);
            }
        };
        return factory.getOrCreate(key, value);
    }

    private Node setLabels(Node node, Collection<String> labels) {
        if (labels==null || labels.isEmpty()) return node;
        for (String label : labels) {
            node.addLabel(DynamicLabel.label(label));
        }
        return node;
    }

    @Override
    public Relationship getOrCreateRelationship(String indexName, String key, Object value, final Node startNode, final Node endNode, final String type, final Map<String, Object> properties) {
        if (indexName ==null || key == null || value==null) throw new IllegalArgumentException("Unique index "+ indexName +" key "+key+" value must not be null");
        if (startNode ==null || endNode == null || type==null) throw new IllegalArgumentException("StartNode "+ startNode +" EndNode "+ endNode +" and type "+type+" must not be null");
        if (value instanceof Number) value= ValueContext.numeric((Number)value);
        UniqueFactory.UniqueRelationshipFactory factory = new UniqueFactory.UniqueRelationshipFactory(delegate, indexName) {
            @Override
            protected Relationship create(Map<String, Object> _) {
                final Relationship relationship = startNode.createRelationshipTo(endNode, DynamicRelationshipType.withName(type));
                return setProperties(relationship, properties);
            }
        };
        return factory.getOrCreate(key, value);
    }
}
TOP

Related Classes of org.springframework.data.neo4j.support.DelegatingGraphDatabase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.