ch.agent.crnickl.mongodb.WriteMethodsForSchema Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of crnickl-mongodb Show documentation
Show all versions of crnickl-mongodb Show documentation
MongoDB implementation of the CrNiCKL database
/*
* Copyright 2012-2013 Hauser Olsson GmbH
*
* 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 ch.agent.crnickl.mongodb;
import java.util.Collection;
import org.bson.types.ObjectId;
import ch.agent.crnickl.T2DBException;
import ch.agent.crnickl.T2DBMsg;
import ch.agent.crnickl.T2DBMsg.E;
import ch.agent.crnickl.api.AttributeDefinition;
import ch.agent.crnickl.api.DBObjectId;
import ch.agent.crnickl.api.DBObjectType;
import ch.agent.crnickl.api.Database;
import ch.agent.crnickl.api.Property;
import ch.agent.crnickl.api.Schema;
import ch.agent.crnickl.api.SeriesDefinition;
import ch.agent.crnickl.api.Surrogate;
import ch.agent.crnickl.api.UpdatableSchema;
import ch.agent.crnickl.impl.Permission;
import ch.agent.crnickl.impl.SchemaUpdatePolicy;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.WriteConcern;
/**
* A stateless object with methods providing write access to schemas.
*
* A schema in MongoDB is stored as:
*
*
*
* { _id : OID,
* name : STRING,
* base? : OID,
* attribs : [
* {
* number : NUMBER,
* erasing? : BOOLEAN
* prop? : OID,
* value? : STRING
* }, ...
* ]
* series : [
* {
* number : NUMBER,
* erasing? : BOOLEAN
* description? : STRING,
* attribs : [
* ...
* ]
* }, ...
* ]
* }
*
*
*
*
* Note that attribute and series definitions are not keyed,
* because they do not always have a name (erasing). The position inside the
* array is independent of number. When erasing is present, it is true, and all
* other fields except number are absent. When erasing is absent, the presence
* of other fields denotes overriding behavior. When present, the base
* field identifies the base schema. The attribs and series arrays can be empty
* but are present.
*
* @author Jean-Paul Vetterli
*/
public class WriteMethodsForSchema extends ReadMethodsForSchema {
public WriteMethodsForSchema() {
}
/**
* Create an empty schema in the database.
* Throw an exception if the operation cannot be done.
*
* @param schema a schema
* @throws T2DBException
*/
public void createSchema(UpdatableSchema schema) throws T2DBException {
Surrogate surrogate = null;
Throwable cause = null;
try {
check(Permission.CREATE, schema);
Schema base = schema.getBase();
if (base != null)
check(Permission.READ, base);
DBObjectId ox = insert(schema);
surrogate = makeSurrogate(schema, ox);
} catch (Exception e) {
cause = e;
} finally {
}
if (surrogate == null || cause != null)
throw T2DBMsg.exception(cause, E.E30122, schema.getName());
schema.getSurrogate().upgrade(surrogate);
}
/**
* Delete a schema from the database.
* Throw an exception if the operation cannot be done.
*
* @param schema a schema
* @param policy a schema udpdating policy
* @throws T2DBException
*/
public void deleteSchema(UpdatableSchema schema, SchemaUpdatePolicy policy) throws T2DBException {
boolean done = false;
Throwable cause = null;
Surrogate s = schema.getSurrogate();
MongoDatabase database = (MongoDatabase) s.getDatabase();
try {
check(Permission.MODIFY, schema);
policy.willDelete(schema);
UpdatableSchema original = database.getReadMethodsForSchema().getSchema(s);
DBCollection coll = getMongoDB(s).getSchemas();
coll.remove(asQuery(s.getId()), WriteConcern.SAFE);
database.sleep();
try {
policy.willDelete(schema);
} catch (T2DBException e) {
// Oops! referential integrity broken!
createSchema(original);
throw e;
}
done = true;
} catch (Exception e) {
cause = e;
} finally {
}
if (!done || cause != null) {
throw T2DBMsg.exception(cause, E.E30123, schema.getName());
}
}
/**
* Update the basic schema setup in the database.
* Throw an exception if the operation cannot be done.
*
* @param schema a schema
* @param policy
* @return true if the schema was updated
* @throws T2DBException
*/
public boolean updateSchema(UpdatableSchema schema, SchemaUpdatePolicy policy) throws T2DBException {
boolean done = false;
Throwable cause = null;
Surrogate s = schema.getSurrogate();
MongoDatabase database = (MongoDatabase) s.getDatabase();
try {
check(Permission.MODIFY, schema);
UpdatableSchema original = database.getReadMethodsForSchema().getSchema(s);
Schema base = schema.getBase();
if (base != null && !base.equals(original.getBase()))
check(Permission.READ, base);
DBCollection coll = getMongoDB(s).getSchemas();
// full replace (no need to set _id in full update)
com.mongodb.DBObject operation = mongoObject(
MongoDatabase.FLD_SCHEMA_NAME, schema.getName(),
MongoDatabase.FLD_SCHEMA_BASE, getIdOrZero(schema.getBase()),
MongoDatabase.FLD_SCHEMA_ATTRIBS,
attributeDefinitions(schema.getAttributeDefinitions()),
MongoDatabase.FLD_SCHEMA_SERIES,
seriesDefinitions(schema.getSeriesDefinitions()));
coll.update(asQuery(s.getId()), operation, false, false, WriteConcern.SAFE);
database.sleep();
try {
policy.willUpdate(schema);
} catch (T2DBException e) {
// Oops! referential integrity broken!
operation = mongoObject(
MongoDatabase.FLD_SCHEMA_NAME, original.getName(),
MongoDatabase.FLD_SCHEMA_BASE, getId(original.getBase()),
MongoDatabase.FLD_SCHEMA_ATTRIBS, original.getAttributeDefinitions(),
MongoDatabase.FLD_SCHEMA_SERIES, original.getSeriesDefinitions());
coll.update(asQuery(s.getId()), operation);
throw e;
}
done = true;
} catch (Exception e) {
cause = e;
} finally {
}
if (cause != null)
throw T2DBMsg.exception(cause, E.E30122, schema.getName());
return done;
}
/**
* Find a chronicle referencing one of the schemas.
* This looks like a "reading" method but is used in the context of schema updating.
*
* @param schema a schema
* @return a surrogate or null
* @throws T2DBException
*/
public Surrogate findChronicle(Schema schema) throws T2DBException {
Surrogate result = null;
try {
Database db = schema.getDatabase();
DBObject query = new BasicDBObject();
query.put(MongoDatabase.FLD_CHRON_SCHEMA, getId(schema));
DBObject obj = getMongoDB(db).getChronicles().findOne(query);
if (obj != null)
result = makeSurrogate(db, DBObjectType.CHRONICLE, getObjectId((BasicDBObject)obj));
} catch (Exception e) {
throw T2DBMsg.exception(e, E.E30117);
}
return result;
}
/**
* Find a chronicle with an explicit attribute value for a given property and schemas.
* This looks like a "reading" method but is used in the context of schema updating.
*
* @param property a property
* @param schema a schema
* @return a surrogate or null
* @throws T2DBException
*/
public Surrogate findChronicle(Property> property, Schema schema) throws T2DBException {
Surrogate result = null;
DBCursor cursor = null;
try {
Database db = schema.getDatabase();
cursor = getMongoDB(db).getAttributes().find(mongoObject(
MongoDatabase.FLD_ATTR_PROP, getId(property)));
while (cursor.hasNext()) {
ObjectId chrOid = ((BasicDBObject) cursor.next()).getObjectId(MongoDatabase.FLD_ATTR_CHRON);
Surrogate entityKey = makeSurrogate(db, DBObjectType.CHRONICLE, chrOid);
Schema s = db.getChronicle(entityKey).getSchema(true);
if (s.dependsOnSchema(schema)) {
result = entityKey;
break;
}
}
} catch (Exception e) {
throw T2DBMsg.exception(e, E.E30117);
} finally {
if (cursor != null)
cursor.close();
}
return result;
}
/**
* Find a chronicle with a given series in a collection of schemas.
* This looks like a "reading" method but is used in the context of schema updating.
*
* @param ss a series definition
* @param schema a schema
* @return a surrogate or null
* @throws T2DBException
*/
public Surrogate findChronicle(SeriesDefinition ss, Schema schema) throws T2DBException {
Surrogate result = null;
DBCursor cursor1 = null;
DBCursor cursor2 = null;
try {
Database db = schema.getDatabase();
cursor1 = getMongoDB(db).getChronicles().find(mongoObject(
MongoDatabase.FLD_CHRON_SCHEMA, getId(schema)));
OUTER: while (cursor1.hasNext()) {
ObjectId chronicleOid = getObjectId((BasicDBObject) cursor1.next());
cursor2 = getMongoDB(db).getSeries().find(mongoObject(
MongoDatabase.FLD_SER_CHRON, chronicleOid,
MongoDatabase.FLD_SER_NUM, ss.getNumber()));
while (cursor2.hasNext()) {
Surrogate entityKey = makeSurrogate(db, DBObjectType.CHRONICLE, chronicleOid);
Schema s = db.getChronicle(entityKey).getSchema(true);
if (s.dependsOnSchema(schema)) {
result = entityKey;
break OUTER;
}
}
}
} catch (Exception e) {
throw T2DBMsg.exception(e, E.E30117);
} finally {
if (cursor1 != null)
cursor1.close();
if (cursor2 != null)
cursor2.close();
}
return result;
}
private DBObjectId insert(UpdatableSchema schema) throws T2DBException {
com.mongodb.BasicDBObject bo = new BasicDBObject();
Surrogate s = schema.getSurrogate();
if (!s.inConstruction())
bo.put(MongoDatabase.FLD_ID, getId(schema));
bo.put(MongoDatabase.FLD_SCHEMA_NAME, schema.getName());
bo.put(MongoDatabase.FLD_SCHEMA_BASE, getIdOrZero(schema.getBase()));
bo.put(MongoDatabase.FLD_SCHEMA_ATTRIBS, attributeDefinitions(schema.getAttributeDefinitions()));
bo.put(MongoDatabase.FLD_SCHEMA_SERIES, seriesDefinitions(schema.getSeriesDefinitions()));
getMongoDB(s).getSchemas().insert(bo);
ObjectId ox = getObjectId(bo);
return new MongoDBObjectId(ox);
}
private BasicDBList attributeDefinitions(
Collection> defs) throws T2DBException {
BasicDBList list = new BasicDBList();
if (defs != null) {
int i = 0;
for (AttributeDefinition> def : defs) {
list.put(i++, attributeDefinition(def));
}
}
return list;
}
private BasicDBObject attributeDefinition(AttributeDefinition> def) throws T2DBException {
com.mongodb.BasicDBObject bo = new BasicDBObject();
bo.put(MongoDatabase.FLD_ATTRIBDEF_NUM, def.getNumber());
bo.put(MongoDatabase.FLD_ATTRIBDEF_ERASING, def.isErasing());
bo.put(MongoDatabase.FLD_ATTRIBDEF_PROP, def.isErasing() ? null : getId(def.getProperty()));
bo.put(MongoDatabase.FLD_ATTRIBDEF_VAL, def.isErasing() ? null :
def.getProperty().getValueType().toString(def.getValue()));
return bo;
}
private BasicDBList seriesDefinitions(Collection defs) throws T2DBException {
BasicDBList list = new BasicDBList();
if (defs != null) {
int i = 0;
for (SeriesDefinition def : defs) {
list.put(i++, seriesDefinition(def));
}
}
return list;
}
private BasicDBObject seriesDefinition(SeriesDefinition def) throws T2DBException {
com.mongodb.BasicDBObject bo = new BasicDBObject();
bo.put(MongoDatabase.FLD_SERIESDEF_NUM, def.getNumber());
bo.put(MongoDatabase.FLD_SERIESDEF_ERASING, def.isErasing());
bo.put(MongoDatabase.FLD_SERIESDEF_DESC, def.isErasing() ? null : def.getDescription());
bo.put(MongoDatabase.FLD_SERIESDEF_ATTRIBS, def.isErasing() ? attributeDefinitions(null) :
attributeDefinitions(def.getAttributeDefinitions()));
return bo;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy