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

com.arangodb.BaseArangoDriver Maven / Gradle / Ivy

There is a newer version: 7.15.0
Show newest version
/*
 * Copyright (C) 2012 tamtam180
 *
 * 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 com.arangodb;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import com.arangodb.entity.BaseEntity;
import com.arangodb.entity.DefaultEntity;
import com.arangodb.entity.DocumentEntity;
import com.arangodb.entity.EntityDeserializers;
import com.arangodb.entity.EntityFactory;
import com.arangodb.entity.KeyValueEntity;
import com.arangodb.entity.ReplicationDumpHeader;
import com.arangodb.entity.StreamEntity;
import com.arangodb.entity.marker.MissingInstanceCreater;
import com.arangodb.http.HttpResponseEntity;
import com.arangodb.util.DateUtils;
import com.arangodb.util.ReflectionUtils;
import com.arangodb.util.StringUtils;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;

/**
 * @author tamtam180 - kirscheless at gmail.com
 *
 */
public abstract class BaseArangoDriver {

	private static final Pattern databaseNamePattern = Pattern.compile("^[a-zA-Z][a-zA-Z0-9\\-_]{0,63}$");

	protected String createDocumentHandle(long collectionId, String documentKey) {
		return collectionId + "/" + documentKey;
	}

	protected String createDocumentHandle(String collectionName, String documentKey) throws ArangoException {
		validateCollectionName(collectionName);
		return collectionName + "/" + documentKey;
	}

	protected void validateCollectionName(String name) throws ArangoException {
		if (name.indexOf('/') != -1) {
			throw new ArangoException("does not allow '/' in name.");
		}
	}

	protected void validateDocumentHandle(String documentHandle) throws ArangoException {
		int pos = documentHandle.indexOf('/');
		if (pos > 0) {
			String collectionName = documentHandle.substring(0, pos);
			String documentKey = documentHandle.substring(pos + 1);

			validateCollectionName(collectionName);
			if (collectionName.length() != 0 && documentKey.length() != 0) {
				return;
			}
		}
		throw new ArangoException("invalid format documentHandle:" + documentHandle);
	}

	/**
	 * @param database
	 * @param allowNull
	 * @throws ArangoException
	 * @see 
	 *      DatabaseNames documentation
	 */
	protected void validateDatabaseName(String database, boolean allowNull) throws ArangoException {
		boolean valid = false;
		if (database == null) {
			if (allowNull) {
				valid = true;
			}
		} else {
			valid = databaseNamePattern.matcher(database).matches();
			if ("_system".equals(database)) {
				valid = true;
			}
		}
		if (!valid) {
			throw new ArangoException("invalid format database:" + database);
		}
	}

	protected void setKeyValueHeader(HttpResponseEntity res, KeyValueEntity entity) throws ArangoException {

		Map headers = res.getHeaders();

		try {
			String strCreated = headers.get("x-voc-created");
			if (strCreated != null) {
				entity.setCreated(DateUtils.parse(strCreated, "yyyy-MM-dd'T'HH:mm:ss'Z'"));
			}

			String strExpires = headers.get("x-voc-expires");
			if (strExpires != null) {
				entity.setExpires(DateUtils.parse(strExpires, "yyyy-MM-dd'T'HH:mm:ss'Z'"));
			}

			String strExtened = headers.get("x-voc-extended");
			if (strExtened != null) {
				Map attributes = EntityFactory.createEntity(strExtened, Map.class);
				entity.setAttributes(attributes);
			}

		} catch (ParseException e) {
			throw new ArangoException(e);
		}

	}

	protected ReplicationDumpHeader toReplicationDumpHeader(HttpResponseEntity res) {
		ReplicationDumpHeader header = new ReplicationDumpHeader();

		Map headerMap = res.getHeaders();
		String value;

		value = headerMap.get("x-arango-replication-active");
		if (value != null) {
			header.setActive(Boolean.parseBoolean(value));
		}

		value = headerMap.get("x-arango-replication-lastincluded");
		if (value != null) {
			header.setLastincluded(Long.parseLong(value));
		}

		value = headerMap.get("x-arango-replication-lasttick");
		if (value != null) {
			header.setLasttick(Long.parseLong(value));
		}

		value = headerMap.get("x-arango-replication-checkmore");
		if (value != null) {
			header.setCheckmore(Boolean.parseBoolean(value));
		}

		return header;
	}

	/**
	 * Checks the Http response for database or server errors
	 * 
	 * @param res
	 *            the response of the database
	 * @return The Http status code
	 * @throws ArangoException
	 *             if any error happened
	 */
	private int checkServerErrors(HttpResponseEntity res) throws ArangoException {
		int statusCode = res.getStatusCode();

		if (statusCode >= 400) { // always throws ArangoException
			DefaultEntity defaultEntity = new DefaultEntity();
			if (res.getText() != null && !"".equals(res.getText()) && statusCode != 500) {
				JsonParser jsonParser = new JsonParser();
				JsonElement jsonElement = jsonParser.parse(res.getText());
				JsonObject jsonObject = jsonElement.getAsJsonObject();
				JsonElement errorMessage = jsonObject.get("errorMessage");
				defaultEntity.setErrorMessage(errorMessage.getAsString());
				JsonElement errorNumber = jsonObject.get("errorNum");
				defaultEntity.setErrorNumber(errorNumber.getAsInt());
			} else {
				defaultEntity.setErrorMessage(res.createStatusPhrase());
			}

			defaultEntity.setCode(statusCode);
			defaultEntity.setStatusCode(statusCode);
			defaultEntity.setError(true);
			ArangoException arangoException = new ArangoException(defaultEntity);
			arangoException.setCode(statusCode);
			throw arangoException;
		}

		return statusCode;
	}

	/**
	 * Creates an entity object
	 * 
	 * @param res
	 *            the response of the database
	 * @param clazz
	 *            the class of the entity object
	 * @param pclazz
	 *            the class of the object wrapped in the entity object
	 * @param validate
	 *            true for validation
	 * @return the result entity object of class T (T extends BaseEntity)
	 * @throws ArangoException
	 */
	protected  T createEntity(
		HttpResponseEntity res,
		Class clazz,
		Class[] pclazz,
		boolean validate) throws ArangoException {
		if (res == null) {
			return null;
		}
		boolean isDocumentEntity = false;

		// the following was added to ensure, that attributes with a key like
		// "error", "code", "errorNum"
		// and "etag" will be serialized, when no error was thrown by the
		// database
		if (clazz == DocumentEntity.class) {
			isDocumentEntity = true;
		}

		int statusCode = checkServerErrors(res);

		try {
			EntityDeserializers.setParameterized(pclazz);

			T entity = createEntityWithFallback(res, clazz);

			setStatusCode(res, entity);
			if (validate) {
				validate(res, entity);
			}

			if (isDocumentEntity) { // && requestSuccessful NOTE: no need for
									// this, an exception is always thrown
				entity.setCode(statusCode);
				entity.setErrorMessage(null);
				entity.setError(false);
				entity.setErrorNumber(0);
			}

			return entity;
		} finally {
			EntityDeserializers.removeParameterized();
		}
	}

	private  T createEntityWithFallback(HttpResponseEntity res, Class clazz)
			throws ArangoException {
		T entity = createEntityImpl(res, clazz);
		if (entity == null) {
			Class c = MissingInstanceCreater.getMissingClass(clazz);
			entity = ReflectionUtils.newInstance(c);
		} else if (res.isBatchRepsonse()) {
			try {
				entity = clazz.newInstance();
			} catch (Exception e) {
				throw new ArangoException(e);
			}
		}
		return entity;
	}

	/**
	 * Gets the raw JSON string with results, from the Http response
	 * 
	 * @param res
	 *            the response of the database
	 * @return A valid JSON string with the results
	 * @throws ArangoException
	 */
	protected String getJSONResponseText(HttpResponseEntity res) throws ArangoException {
		if (res == null) {
			return null;
		}

		checkServerErrors(res);

		// no errors, return results as a JSON string
		JsonParser jsonParser = new JsonParser();
		JsonElement jsonElement = jsonParser.parse(res.getText());
		JsonObject jsonObject = jsonElement.getAsJsonObject();
		JsonElement result = jsonObject.get("result");
		return result.toString();
	}

	protected  T createEntity(String str, Class clazz, Class... pclazz) throws ArangoException {
		try {
			EntityDeserializers.setParameterized(pclazz);
			return EntityFactory.createEntity(str, clazz);
		} finally {
			EntityDeserializers.removeParameterized();
		}
	}

	protected  T createEntity(HttpResponseEntity res, Class clazz) throws ArangoException {
		return createEntity(res, clazz, null, true);
	}

	protected  T createEntity(HttpResponseEntity res, Class clazz, Class... pclazz)
			throws ArangoException {
		return createEntity(res, clazz, pclazz, true);
	}

	protected void setStatusCode(HttpResponseEntity res, BaseEntity entity) throws ArangoException {
		if (entity != null) {
			if (res.getEtag() > 0) {
				entity.setEtag(res.getEtag());
			}
			entity.setStatusCode(res.getStatusCode());
			if (res.getRequestId() != null) {
				entity.setRequestId(res.getRequestId());
			}
		}
	}

	protected void validate(HttpResponseEntity res, BaseEntity entity) throws ArangoException {

		if (entity != null && entity.isError()) {
			throw new ArangoException(entity);
		}

		// Custom Error
		if (res.getStatusCode() >= 400) {

			BaseEntity tmpEntity = entity;
			if (tmpEntity == null) {
				tmpEntity = new DefaultEntity();
			}

			if (res.isTextResponse()) {
				tmpEntity.setErrorNumber(res.getStatusCode());
				tmpEntity.setErrorMessage(res.getText());
			} else {
				tmpEntity.setErrorNumber(res.getStatusCode());
				tmpEntity.setErrorMessage(res.getStatusPhrase());
			}

			switch (res.getStatusCode()) {
			case 401:
				tmpEntity.setErrorMessage("Unauthorized");
				break;
			case 403:
				tmpEntity.setErrorMessage("Forbidden");
				break;
			default:
			}

			throw new ArangoException(tmpEntity);
		}
	}

	@SuppressWarnings("unchecked")
	protected  T createEntityImpl(HttpResponseEntity res, Class type) throws ArangoException {
		T result = null;
		if (res.isJsonResponse()) {
			try {
				result = EntityFactory.createEntity(res.getText(), type);
			} catch (JsonSyntaxException e) {
				throw new ArangoException("got JsonSyntaxException while creating entity", e);
			} catch (JsonParseException e) {
				throw new ArangoException("got JsonParseException while creating entity", e);
			}
		} else if (res.isDumpResponse() && StreamEntity.class.isAssignableFrom(type)) {
			result = (T) new StreamEntity(res.getStream());
		} else if (StringUtils.isNotEmpty(res.getText())) {
			throw new ArangoException("expected JSON result from server but got: " + res.getText());
		}

		return result;
	}

	protected String createEndpointUrl(String database, Object... paths) throws ArangoException {
		List list = new ArrayList();

		if (database != null) {
			validateDatabaseName(database, false);
			list.add("_db");
			list.add(database);
		}
		for (Object path : paths) {
			if (path != null) {
				list.add(path.toString());
			}
		}
		return StringUtils.join(false, list);
	}

	protected String createEndpointUrl(String database, String str, Object... paths) throws ArangoException {
		if (paths == null) {
			return createEndpointUrl(database, paths);
		}
		Object[] newPaths = new Object[paths.length + 1];
		newPaths[0] = str;
		for (int i = 0; i < paths.length; i++) {
			newPaths[i + 1] = paths[i];
		}
		return createEndpointUrl(database, newPaths);
	}

	protected String createUserEndpointUrl(Object... paths) throws ArangoException {
		return createEndpointUrl(null, "/_api/user", paths);
	}

	protected String createJobEndpointUrl(String database, Object... paths) throws ArangoException {
		return createEndpointUrl(database, "/_api/job", paths);
	}

	protected String createIndexEndpointUrl(String database, Object... paths) throws ArangoException {
		return createEndpointUrl(database, "/_api/index", paths);
	}

	protected String createGharialEndpointUrl(String database, Object... paths) throws ArangoException {
		return createEndpointUrl(database, "/_api/gharial", paths);
	}

	protected String createDocumentEndpointUrl(String database, Object... paths) throws ArangoException {
		return createEndpointUrl(database, "/_api/document", paths);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy