All Downloads are FREE. Search and download functionalities are using the official Maven repository.

iot.jcypher.database.embedded.AbstractEmbeddedDBAccess Maven / Gradle / Ivy

Go to download

Provides seamlessly integrated Java access to graph databases (Neo4J) at different levels of abstraction.

There is a newer version: 4.2.0
Show newest version
/************************************************************************
 * Copyright (c) 2014-2016 IoT-Solutions e.U.
 * 
 * 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 iot.jcypher.database.embedded;

import iot.jcypher.database.IDBAccess;
import iot.jcypher.database.internal.DBUtil;
import iot.jcypher.database.internal.IDBAccessInit;
import iot.jcypher.database.util.QParamsUtil;
import iot.jcypher.query.JcQuery;
import iot.jcypher.query.JcQueryResult;
import iot.jcypher.query.result.JcError;
import iot.jcypher.query.writer.CypherWriter;
import iot.jcypher.query.writer.IQueryParam;
import iot.jcypher.query.writer.QueryParam;
import iot.jcypher.query.writer.QueryParamSet;
import iot.jcypher.query.writer.WriterContext;
import iot.jcypher.transaction.ITransaction;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;

import org.neo4j.driver.v1.AuthToken;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.Result;
import org.neo4j.graphdb.Transaction;

import scala.collection.convert.Wrappers.SeqWrapper;

public abstract class AbstractEmbeddedDBAccess implements IDBAccessInit {

	private ThreadLocal transaction = new ThreadLocal();
	protected Properties properties;
	private GraphDatabaseService graphDb;
	private Thread shutdownHook;
	private boolean registerShutdownHook = true;

	public AbstractEmbeddedDBAccess() {
		super();
	}

	public AbstractEmbeddedDBAccess(GraphDatabaseService gdbs) {
		super();
		this.graphDb = gdbs;
	}

	@Override
	public JcQueryResult execute(JcQuery query) {
		List qList = new ArrayList();
		qList.add(query);
		List qrList = execute(qList);
		return qrList.get(0);
	}

	@Override
	public List execute(List queries) {
		List statements = new ArrayList(queries.size());
		for (JcQuery query : queries) {
			WriterContext context = new WriterContext();
			QueryParam.setExtractParams(query.isExtractParams(), context);
			CypherWriter.toCypherExpression(query, context);
			String cypher = context.buffer.toString();
			Map paramsMap = QParamsUtil.createQueryParams(context);
			statements.add(new Statement(cypher, paramsMap));
		}
		JsonBuilderContext builderContext = new JsonBuilderContext();
		initJsonBuilderContext(builderContext);
		Throwable exception = null;
		GraphDatabaseService engine = null;
		try {
			engine = getGraphDB();
		} catch (Throwable e) {
			exception = e;
		}
		Result result = null;
		Transaction tx = null;
		Throwable dbException = null;
		if (engine != null) {
			ETransactionImpl etx = null;
			if ((etx = this.transaction.get()) != null)
				tx = etx.getTransaction();
			else
				tx = getGraphDB().beginTx();
			try {
				for (Statement statement : statements) {
					if (statement.parameterMap != null)
						result = engine.execute(statement.cypher, statement.parameterMap);
					else
						result = engine.execute(statement.cypher);

					addInnerResultsAndDataArray(builderContext);
					List cols = result.columns();
					addColumns(builderContext, cols);
					while (result.hasNext()) {
						// that is one row
						Map row = result.next();
						addRow(builderContext, row, cols);
					}
				}
				if (etx == null)
					tx.success();
			} catch (Throwable e) {
				dbException = e;
				if (tx != null)
					tx.failure();
			} finally {
				if (etx == null && tx != null) {
					try {
						tx.close();
					} catch (Throwable e1) {
						dbException = e1;
					}
				}
			}
		}

		if (dbException != null) {
			addDBError(builderContext, dbException);
		}
		JsonObject jsonResult = builderContext.build();

		List ret = new ArrayList(queries.size());
		for (int i = 0; i < queries.size(); i++) {
			JcQueryResult qr = new JcQueryResult(jsonResult, i, this);
			ret.add(qr);
			if (exception != null) {
				String typ = exception.getClass().getSimpleName();
				String msg = exception.getLocalizedMessage();
				qr.addGeneralError(new JcError(typ, msg, DBUtil.getStacktrace(exception)));
			}
		}
		return ret;
	}

	@Override
	public List clearDatabase() {
		return DBUtil.clearDatabase(this);
	}

	@Override
	public ITransaction beginTX() {
		ETransactionImpl tx = this.transaction.get();
		if (tx == null) {
			tx = new ETransactionImpl(this);
			this.transaction.set(tx);
		}
		return tx;
	}

	@Override
	public ITransaction getTX() {
		return this.transaction.get();
	}

	@Override
	public boolean isDatabaseEmpty() {
		return DBUtil.isDatabaseEmpty(this);
	}

	@Override
	public synchronized void close() {
		if (this.shutdownHook != null) {
			Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
			this.shutdownHook = null;
		}

		shutDown();
		this.graphDb = null;
	}

	@Override
	public void setAuth(String userId, String password) {
		// nop
	}

	@Override
	public void setAuth(AuthToken authToken) {
		// nop
	}

	@Override
	public IDBAccess removeShutdownHook() {
		this.registerShutdownHook = false;
		if (this.shutdownHook != null) {
			Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
			this.shutdownHook = null;
		}
		return this;
	}

	@Override
	public IDBAccess addShutdownHook() {
		this.registerShutdownHook = true;
		if (this.shutdownHook == null) {
			Thread hook = new Thread() {
				@Override
				public void run() {
					shutDown();
				}
			};
			Runtime.getRuntime().addShutdownHook(hook);
			this.shutdownHook = hook;
		}
		return this;
	}

	protected void shutDown() {
		try {
			if (this.graphDb != null)
				this.graphDb.shutdown();
		} catch (Throwable e) {
			// do nothing
		}
	}

	private void addDBError(JsonBuilderContext builderContext, Throwable exception) {
		String code = exception.getClass().getSimpleName();
		String msg = exception.getLocalizedMessage();
		JsonObjectBuilder errorObject = Json.createObjectBuilder();
		errorObject.add("code", code);
		if (msg == null)
			msg = code;
		errorObject.add("message", msg);
		errorObject.add("info", DBUtil.getStacktrace(exception));
		builderContext.errorsArray.add(errorObject);
	}

	protected abstract GraphDatabaseService createGraphDB();

	public synchronized GraphDatabaseService getGraphDB() {
		if (this.graphDb == null) {
			this.graphDb = createGraphDB();
			this.shutdownHook = registerShutdownHook();
		}
		return this.graphDb;
	}

	private void initJsonBuilderContext(JsonBuilderContext builderContext) {
		builderContext.resultObject = Json.createObjectBuilder();
		builderContext.resultsArray = Json.createArrayBuilder();
		builderContext.errorsArray = Json.createArrayBuilder();
		builderContext.innerResultsObjects = new ArrayList();
		builderContext.dataArrays = new ArrayList();
	}

	private void addInnerResultsAndDataArray(JsonBuilderContext builderContext) {
		builderContext.innerResultsObjects.add(Json.createObjectBuilder());
		builderContext.dataArrays.add(Json.createArrayBuilder());
	}

	private void addColumns(JsonBuilderContext builderContext, List cols) {
		JsonArrayBuilder columns = Json.createArrayBuilder();
		for (String col : cols) {
			columns.add(col);
		}
		builderContext.innerResultsObjects.get(builderContext.innerResultsObjects.size() - 1).add("columns", columns);
	}

	private void addRow(JsonBuilderContext builderContext, Map row, List cols) {

		JsonObjectBuilder rowObject = Json.createObjectBuilder();
		JsonArrayBuilder restArray = Json.createArrayBuilder();
		JsonObjectBuilder graphObject = Json.createObjectBuilder();
		JsonArrayBuilder nodesArray = Json.createArrayBuilder();
		JsonArrayBuilder relationsArray = Json.createArrayBuilder();

		Object restObject;
		List nodes = new ArrayList();
		List relations = new ArrayList();
		for (int i = 0; i < cols.size(); i++) {
			restObject = null;
			String key = cols.get(i);
			Object val = row.get(key);
			restObject = buildRestObject(val, nodes, relations);
			if (restObject == null)
				writeLiteralValue(val, restArray);
			else if (restObject instanceof JsonObjectBuilder)
				restArray.add((JsonObjectBuilder) restObject);
			else if (restObject instanceof JsonArrayBuilder)
				restArray.add((JsonArrayBuilder) restObject);
		}

		Collections.sort(nodes);
		Collections.sort(relations);
		for (NodeHolder nh : nodes) {
			nodesArray.add(nh.nodeObject);
		}
		for (RelationHolder rh : relations) {
			relationsArray.add(rh.relationObject);
		}

		rowObject.add("rest", restArray);
		graphObject.add("nodes", nodesArray);
		graphObject.add("relationships", relationsArray);
		rowObject.add("graph", graphObject);
		builderContext.dataArrays.get(builderContext.dataArrays.size() - 1).add(rowObject);
	}

	private Object buildRestObject(Object val, List nodes, List relations) {
		JsonObjectBuilder restObject = null;
		JsonArrayBuilder jsarr = null;

		if (val instanceof Node) {
			restObject = Json.createObjectBuilder();
			Node node = (Node) val;
			restObject.add("self", "node/".concat(String.valueOf(node.getId())));
			addNode(node, nodes);
		} else if (val instanceof Relationship) {
			restObject = Json.createObjectBuilder();
			Relationship relation = (Relationship) val;
			restObject.add("start", "node/".concat(String.valueOf(relation.getStartNode().getId())));
			restObject.add("self", "relationship/".concat(String.valueOf(relation.getId())));
			restObject.add("end", "node/".concat(String.valueOf(relation.getEndNode().getId())));
			RelationHolder rh = new RelationHolder(relation);
			if (!relations.contains(rh)) {
				RelationHolder.RelationNodes nds = rh.init(relation);
				addNode(nds.startNode, nodes);
				addNode(nds.endNode, nodes);
				relations.add(rh);
			}
		} else if (val instanceof Path) {
			restObject = Json.createObjectBuilder();
			Path path = (Path) val;
			restObject.add("start", "node/".concat(String.valueOf(path.startNode().getId())));
			JsonArrayBuilder pNodesArray = Json.createArrayBuilder();
			Iterator nit = path.nodes().iterator();
			while (nit.hasNext()) {
				Node node = nit.next();
				pNodesArray.add("node/".concat(String.valueOf(node.getId())));
				addNode(node, nodes);
			}
			restObject.add("nodes", pNodesArray);
			restObject.add("length", path.length());
			JsonArrayBuilder pRelationsArray = Json.createArrayBuilder();
			Iterator rit = path.relationships().iterator();
			while (rit.hasNext()) {
				Relationship relation = rit.next();
				pRelationsArray.add("relationship/".concat(String.valueOf(relation.getId())));
				RelationHolder rh = new RelationHolder(relation);
				if (!relations.contains(rh)) {
					rh.init(relation);
					// nodes have already been added for the path
					relations.add(rh);
				}
			}
			restObject.add("relationships", pRelationsArray);
			restObject.add("end", "node/".concat(String.valueOf(path.endNode().getId())));
		} else if (val instanceof SeqWrapper) {
			jsarr = Json.createArrayBuilder();
			int sz = ((SeqWrapper) val).size();
			boolean restFound = false;
			for (int i = 0; i < sz; i++) {
				Object obj = ((SeqWrapper) val).get(i);
				Object ro = buildRestObject(obj, nodes, relations);
				if (ro instanceof JsonObjectBuilder) {
					jsarr.add((JsonObjectBuilder) ro);
					restFound = true;
				} else if (ro instanceof JsonArrayBuilder) {
					jsarr.add((JsonArrayBuilder) ro);
					restFound = true;
				}
			}
			if (!restFound)
				jsarr = null;
		}

		if (restObject != null)
			return restObject;
		if (jsarr != null)
			return jsarr;
		return null;
	}

	private static void writeLiteralValue(Object val, JsonArrayBuilder array) {
		if (val == null)
			array.addNull();
		else if (val instanceof String)
			array.add(val.toString());
		else if (val instanceof Number) {
			if (val instanceof Long)
				array.add(((Long) val).longValue());
			else if (val instanceof Integer)
				array.add(((Integer) val).intValue());
			else if (val instanceof Double)
				array.add(((Double) val).doubleValue());
			else if (val instanceof Float)
				array.add(((Float) val).floatValue());
		} else if (val instanceof Boolean) {
			array.add(((Boolean) val).booleanValue());
		} else if (val != null && val.getClass().isArray()) {
			JsonArrayBuilder jsarr = Json.createArrayBuilder();
			if (val instanceof int[]) {
				int[] arr = (int[]) val;
				for (int v : arr) {
					writeLiteralValue(v, jsarr);
				}
			} else if (val instanceof long[]) {
				long[] arr = (long[]) val;
				for (long v : arr) {
					writeLiteralValue(v, jsarr);
				}
			} else if (val instanceof float[]) {
				float[] arr = (float[]) val;
				for (float v : arr) {
					writeLiteralValue(v, jsarr);
				}
			} else if (val instanceof double[]) {
				double[] arr = (double[]) val;
				for (double v : arr) {
					writeLiteralValue(v, jsarr);
				}
			} else {
				Object[] arr = (Object[]) val;
				for (Object v : arr) {
					writeLiteralValue(v, jsarr);
				}
			}
			array.add(jsarr);
		} else if (val instanceof SeqWrapper) {
			JsonArrayBuilder jsarr = Json.createArrayBuilder();
			int sz = ((SeqWrapper) val).size();
			for (int i = 0; i < sz; i++) {
				Object obj = ((SeqWrapper) val).get(i);
				writeLiteralValue(obj, jsarr);
			}
			array.add(jsarr);
		}
	}

	private void addNode(Node node, List nodes) {
		if (node != null) {
			NodeHolder nh = new NodeHolder(node);
			if (!nodes.contains(nh)) {
				nh.init(node);
				nodes.add(nh);
			}
		}
	}

	void removeTx() {
		this.transaction.remove();
	}

	private Thread registerShutdownHook() {
		// Registers a shutdown hook for the Neo4j instance so that it
		// shuts down nicely when the VM exits (even if you "Ctrl-C" the
		// running application).
		Thread hook = null;
		if (this.registerShutdownHook) {
			hook = new Thread() {
				@Override
				public void run() {
					shutDown();
				}
			};
			Runtime.getRuntime().addShutdownHook(hook);
		}
		return hook;
	}

	/*************************************************/
	private static class JsonBuilderContext {
		private JsonObjectBuilder resultObject;
		private JsonArrayBuilder resultsArray;
		private JsonArrayBuilder errorsArray;
		private List innerResultsObjects;
		private List dataArrays;

		private JsonObject build() {
			for (int i = 0; i < this.innerResultsObjects.size(); i++) {
				this.innerResultsObjects.get(i).add("data", this.dataArrays.get(i));
				this.resultsArray.add(this.innerResultsObjects.get(i));
			}
			this.resultObject.add("results", this.resultsArray);
			this.resultObject.add("errors", this.errorsArray);
			return this.resultObject.build();
		}
	}

	/*************************************************/
	private static class NodeHolder implements Comparable {

		private long id;
		private JsonObjectBuilder nodeObject;

		private NodeHolder(Node node) {
			super();
			this.id = node.getId();
		}

		private void init(Node node) {
			JsonObjectBuilder nd = Json.createObjectBuilder();
			nd.add("id", String.valueOf(node.getId()));
			JsonArrayBuilder labels = Json.createArrayBuilder();
			Iterator




© 2015 - 2024 Weber Informatics LLC | Privacy Policy