package org.arangodb.objectmapper;
//////////////////////////////////////////////////////////////////////////////////////////
//
//Object mapper for ArangoDB by triAGENS GmbH Cologne.
//
//Copyright triAGENS GmbH Cologne.
//
//////////////////////////////////////////////////////////////////////////////////////////
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import org.arangodb.objectmapper.http.ArangoDbHttpClient;
import org.arangodb.objectmapper.http.ArangoDbHttpResponse;
import org.arangodb.objectmapper.http.ResponseCallback;
import org.arangodb.objectmapper.http.RestHandler;
import org.arangodb.objectmapper.http.RevisionResponse;
import org.arangodb.objectmapper.jackson.ArangoDbDocument;
import org.arangodb.objectmapper.jackson.JsonSerializer;
import org.arangodb.objectmapper.util.ArangoDbAssert;
import org.arangodb.objectmapper.ServerRole;
import com.fasterxml.jackson.databind.JsonNode;
public class Database {
/**
* rest handler
*/
private final RestHandler rest;
/**
* the json serializer (jackson)
*/
private final JsonSerializer serializer;
/**
* Database name
*/
private final String name;
/**
* Database prefix
*/
public final static String DB_PREFIX = "/_db/";
/**
* Path to document api
*/
public final static String DOCUMENT_PATH = "/_api/document/";
/**
* Path to version api
*/
public final static String VERSION_PATH = "/_api/version";
/**
* Path to server role api
*/
public final static String ROLE_PATH = "/_admin/server/role";
/**
* Path to collection api
*/
public final static String COLLECTION_PATH = "/_api/collection";
/**
* Path to database api
*/
public final static String DATABASE_PATH = "/_api/database";
/**
* Path to index api
*/
public final static String INDEX_PATH = "/_api/index";
/**
* Create database
*
* @param client
* a http client connection
*
* @param name
* database name
*/
public Database(ArangoDbHttpClient client, String name) {
this.rest = new RestHandler(client);
this.serializer = new JsonSerializer();
this.name = name;
}
/**
* Create database
*
* @param client
* a http client connection
*/
/*
public Database(ArangoDbHttpClient client) {
this(client, "");
}
*/
/**
* Build a database-specific path
*
* @param relPath the path without the database name
*
* @return String The database-specific path
*/
public String buildPath(String relPath) {
if (this.name == "") {
return relPath;
}
return DB_PREFIX + this.name + relPath;
}
public String getName() {
return name;
}
public Map<String, Object> getAsMap() {
Map<String, Object> result = new HashMap<String, Object>();
result.put("name", name);
return result;
}
/**
* Request the version of ArangoDB
*
* @return Version The version object number
*
* @throws ArangoDb4JException
*/
public Version getVersion() throws ArangoDb4JException {
return rest.get(buildPath(VERSION_PATH), new ResponseCallback<Version>() {
@Override
public Version success(ArangoDbHttpResponse hr)
throws ArangoDb4JException {
return serializer.toObject(hr.getContentAsStream(),
Version.class);
}
});
}
/**
* Request the server role
*
* @return String The server role as an enum value
*/
public ServerRole.roleEnum getServerRole() {
try {
JsonNode root = rest.get(buildPath(ROLE_PATH), new ResponseCallback<JsonNode>() {
@Override
public JsonNode success(ArangoDbHttpResponse hr)
throws ArangoDb4JException {
return serializer.toJsonNode(hr.getContentAsStream());
}
});
if (root != null && root.has("result")) {
return ServerRole.fromString(root.get("result").asText());
}
}
catch (Exception e) {
// this might happen if the server API is too "old"
}
return ServerRole.roleEnum.UNKNOWN;
}
public static Database createDatabase (ArangoDbHttpClient client, String name) throws ArangoDb4JException {
Database db = new Database(client, name);
return db.createDatabase(db);
}
public Database createDatabase (String name) throws ArangoDb4JException {
return createDatabase(new Database(this.rest.getClient(), name));
}
public Database createDatabase (Database db) throws ArangoDb4JException {
ArangoDbAssert.notNull(db, "given database is null");
rest.post("/_db/_system" + DATABASE_PATH, serializer.toJson(db.getAsMap()),
new ResponseCallback<Integer>() {
@Override
public Integer success(ArangoDbHttpResponse hr)
throws ArangoDb4JException {
return null;
}
});
return db;
}
public void deleteDatabase (String name) throws ArangoDb4JException {
final String path = "/_db/_system" + DATABASE_PATH + "/" + encodePart(name);
rest.delete(path);
}
public void deleteDatabase (Database db) throws ArangoDb4JException {
ArangoDbAssert.notNull(db, "given database is null");
db.deleteDatabase(db.name);
}
// /////////////////////////////////////////////////////////////////////////
// CRUD functions
// /////////////////////////////////////////////////////////////////////////
/**
* Save object to ArangoDB
*
* @param o the object to save
*
* @return ArangoDbDocument Returns the given object. (Id, Key and Revision
* is filled)
*
* @throws ArangoDb4JException
*/
public <T extends ArangoDbDocument> T createDocument(T o)
throws ArangoDb4JException {
ArangoDbAssert.notNull(o, "given object is null");
if (!o.isNew()) {
throw new ArangoDb4JException("Document is not new.");
}
String collection = getCollectionName(o.getClass());
String path = DOCUMENT_PATH + "?createCollection=true&collection=" + encodePart(collection);
RevisionResponse resp = rest.post(buildPath(path), serializer.toJson(o),
new ResponseCallback<RevisionResponse>() {
@Override
public RevisionResponse success(ArangoDbHttpResponse hr)
throws ArangoDb4JException {
return serializer.toObject(hr.getContentAsStream(),
RevisionResponse.class);
}
});
o.setId(resp.getId());
o.setKey(resp.getKey());
o.setRevision(resp.getRevision());
return o;
}
/**
* Load object
*
* @param c A class
* @param key The document key
*
* @return T The loaded object
*
* @throws ArangoDb4JException
* on connection, database and unserialize errors
*/
public <T> T readDocument(final Class<T> c, String key)
throws ArangoDb4JException {
ArangoDbAssert.notNull(c, "given class is null");
ArangoDbAssert.hasText(key, "document key is empty");
String collection = getCollectionName(c);
String path = DOCUMENT_PATH + collection + "/" + encodePart(key);
return rest.get(buildPath(path), new ResponseCallback<T>() {
@Override
public T success(ArangoDbHttpResponse hr)
throws ArangoDb4JException {
return serializer.toObject(hr.getContentAsStream(), c);
}
});
}
/**
* Update object
*
* @param o the object to update
*
* @return T Returns the given object. (new Revision
* is filled)
*
* @throws ArangoDb4JException
* on connection, database and unserialize errors
*/
public <T extends ArangoDbDocument> T updateDocument(T o) throws ArangoDb4JException {
return updateDocument(o, true);
}
/**
* Update object
*
* @param o the object to update
*
* @param keepNull whether or not null values remove attributes
*
* @return T Returns the given object. (new Revision
* is filled)
*
* @throws ArangoDb4JException
* on connection, database and unserialize errors
*/
public <T extends ArangoDbDocument> T updateDocument(T o, boolean keepNull)
throws ArangoDb4JException {
ArangoDbAssert.notNull(o, "given object is null");
if (o.isNew()) {
throw new ArangoDb4JException("Document is new.");
}
String collection = getCollectionName(o.getClass());
String path = DOCUMENT_PATH + collection + "/" + encodePart(o.getKey()) +
"?keepNull=" + (keepNull ? "true" : "false");
// TODO: add revision check
RevisionResponse resp = rest.patch(buildPath(path), serializer.toJson(o),
new ResponseCallback<RevisionResponse>() {
@Override
public RevisionResponse success(ArangoDbHttpResponse hr)
throws ArangoDb4JException {
return serializer.toObject(hr.getContentAsStream(),
RevisionResponse.class);
}
});
o.setRevision(resp.getRevision());
return o;
}
/**
* Replace object
*
* @param o the object to replace
*
* @return T Returns the given object. (new Revision
* is filled)
*
* @throws ArangoDb4JException
* on connection, database and unserialize errors
*/
public <T extends ArangoDbDocument> T replaceDocument(T o)
throws ArangoDb4JException {
ArangoDbAssert.notNull(o, "given object is null");
if (o.isNew()) {
throw new ArangoDb4JException("Document is new.");
}
String collection = getCollectionName(o.getClass());
String path = DOCUMENT_PATH + collection + "/" + encodePart(o.getKey());
// TODO: add revision check
RevisionResponse resp = rest.put(buildPath(path), serializer.toJson(o),
new ResponseCallback<RevisionResponse>() {
@Override
public RevisionResponse success(ArangoDbHttpResponse hr)
throws ArangoDb4JException {
return serializer.toObject(hr.getContentAsStream(),
RevisionResponse.class);
}
});
o.setRevision(resp.getRevision());
return o;
}
/**
* Delete object
*
* @param o the object to delete
*
* @throws ArangoDb4JException
* on connection, database and unserialize errors
*/
public void deleteDocument(ArangoDbDocument o) throws ArangoDb4JException {
ArangoDbAssert.notNull(o, "given object is null");
if (o.isNew()) {
// nothing to do
return;
}
String collection = getCollectionName(o.getClass());
String path = DOCUMENT_PATH + collection + "/" + encodePart(o.getKey());
// TODO: add revision check
rest.delete(buildPath(path),
new ResponseCallback<RevisionResponse>() {
@Override
public RevisionResponse success(ArangoDbHttpResponse hr)
throws ArangoDb4JException {
return serializer.toObject(hr.getContentAsStream(),
RevisionResponse.class);
}
});
}
// /////////////////////////////////////////////////////////////////////////
// cursor and simple queries
// /////////////////////////////////////////////////////////////////////////
public <T extends ArangoDbDocument> ArangoDbQuery<T> getQuery(final Class<T> c) {
return new ArangoDbQuery<T>(this, c);
}
public <T extends ArangoDbDocument> Cursor<T> getAll (final Class<T> c) throws ArangoDb4JException {
return getAll (c, null);
}
public <T extends ArangoDbDocument> Cursor<T> getAll (final Class<T> c, Long max) throws ArangoDb4JException {
ArangoDbAssert.notNull(c, "given class is null");
ArangoDbQuery<T> query = new ArangoDbQuery<T>(this, c);
if (null != max) {
query.limit(max);
}
return query.execute();
}
// /////////////////////////////////////////////////////////////////////////
// Indices
// /////////////////////////////////////////////////////////////////////////
public <T extends ArangoDbDocument> Index createIndex (final Class<T> c, Index index) throws ArangoDb4JException {
return createIndex(new Collection(c), index);
}
public Index createIndex (String name, Index index) throws ArangoDb4JException {
return createIndex(new Collection(name), index);
}
public Index createIndex (Collection coll, Index index) throws ArangoDb4JException {
ArangoDbAssert.notNull(coll, "given collection is null");
ArangoDbAssert.notNull(index, "given index is null");
String path = INDEX_PATH + "?collection=" + encodePart(coll.getName());
JsonNode root = rest.post(buildPath(path), serializer.toJson(index.getAsMap()),
new ResponseCallback<JsonNode>() {
@Override
public JsonNode success(ArangoDbHttpResponse hr)
throws ArangoDb4JException {
return serializer.toJsonNode(hr.getContentAsStream());
}
});
index.setValues(root);
return index;
}
public Index readIndex (String id) throws ArangoDb4JException {
Index index = new Index();
index.setId(id);
return readIndex(index);
}
public Index readIndex (Index index) throws ArangoDb4JException {
ArangoDbAssert.notNull(index, "given collection is null");
String path = INDEX_PATH + "/" + index.getId();
JsonNode root = rest.get(buildPath(path),
new ResponseCallback<JsonNode>() {
@Override
public JsonNode success(ArangoDbHttpResponse hr)
throws ArangoDb4JException {
return serializer.toJsonNode(hr.getContentAsStream());
}
});
index.setValues(root);
return index;
}
public <T extends ArangoDbDocument> List<Index> readIndexList (final Class<T> c) throws ArangoDb4JException {
return readIndexList(new Collection(c));
}
public List<Index> readIndexList (String name) throws ArangoDb4JException {
return readIndexList(new Collection(name));
}
public List<Index> readIndexList (Collection coll) throws ArangoDb4JException {
ArangoDbAssert.notNull(coll, "given collection is null");
String path = INDEX_PATH + "?collection=" + encodePart(coll.getName());
JsonNode root = rest.get(buildPath(path),
new ResponseCallback<JsonNode>() {
@Override
public JsonNode success(ArangoDbHttpResponse hr)
throws ArangoDb4JException {
return serializer.toJsonNode(hr.getContentAsStream());
}
});
return Index.createIndexList(root);
}
public <T extends ArangoDbDocument> void deleteIndex (String id) throws ArangoDb4JException {
Index index = new Index();
index.setId(id);
deleteIndex(index);
}
public void deleteIndex (Index index) throws ArangoDb4JException {
ArangoDbAssert.notNull(index, "given index is null");
String path = INDEX_PATH + "/" + index.getId();
rest.delete(buildPath(path));
}
// /////////////////////////////////////////////////////////////////////////
// Collections
// /////////////////////////////////////////////////////////////////////////
public <T extends ArangoDbDocument> Collection createCollection (final Class<T> c) throws ArangoDb4JException {
return createCollection(new Collection(c));
}
public Collection createCollection (String name) throws ArangoDb4JException {
return createCollection(new Collection(name));
}
public Collection createCollection (Collection coll) throws ArangoDb4JException {
ArangoDbAssert.notNull(coll, "given collection is null");
rest.post(buildPath(COLLECTION_PATH), serializer.toJson(coll.getAsMap()),
new ResponseCallback<Integer>() {
@Override
public Integer success(ArangoDbHttpResponse hr)
throws ArangoDb4JException {
return null;
}
});
String path = COLLECTION_PATH + "/" + encodePart(coll.getName()) + "/properties";
JsonNode root = rest.get(buildPath(path),
new ResponseCallback<JsonNode>() {
@Override
public JsonNode success(ArangoDbHttpResponse hr)
throws ArangoDb4JException {
return serializer.toJsonNode(hr.getContentAsStream());
}
});
coll.setValues(root);
return coll;
}
public <T extends ArangoDbDocument> Collection readCollection (final Class<T> c) throws ArangoDb4JException {
return readCollection(new Collection(c));
}
public Collection readCollection (String name) throws ArangoDb4JException {
return readCollection(new Collection(name));
}
public Collection readCollection (Collection coll) throws ArangoDb4JException {
ArangoDbAssert.notNull(coll, "given collection is null");
String path = COLLECTION_PATH + "/" + encodePart(coll.getName()) + "/properties";
JsonNode root = rest.get(buildPath(path),
new ResponseCallback<JsonNode>() {
@Override
public JsonNode success(ArangoDbHttpResponse hr)
throws ArangoDb4JException {
return serializer.toJsonNode(hr.getContentAsStream());
}
});
coll.setValues(root);
return coll;
}
public <T extends ArangoDbDocument> void deleteCollection (final Class<T> c) throws ArangoDb4JException {
deleteCollection(new Collection(c));
}
public void deleteCollection (String name) throws ArangoDb4JException {
deleteCollection(new Collection(name));
}
public void deleteCollection (Collection coll) throws ArangoDb4JException {
ArangoDbAssert.notNull(coll, "given collection is null");
String path = COLLECTION_PATH + "/" + encodePart(coll.getName());
rest.delete(buildPath(path));
}
private String encodePart (String value) throws ArangoDb4JException {
try {
return URLEncoder.encode(value, "UTF-8");
}
catch (UnsupportedEncodingException e) {
throw new ArangoDb4JException("unexpected failure in encodePart");
}
}
// /////////////////////////////////////////////////////////////////////////
// Utilities
// /////////////////////////////////////////////////////////////////////////
public static <T> String getCollectionName (final Class<T> c) {
return c.getName().replace('.', '_');
}
public JsonSerializer getSerializer() {
return serializer;
}
public RestHandler getRestHandler() {
return rest;
}
}