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

ch.agent.crnickl.mongodb.WriteMethodsForChroniclesAndSeries Maven / Gradle / Ivy

There is a newer version: 2.0.1
Show newest version
/*
 *   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.HashMap;

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.Chronicle;
import ch.agent.crnickl.api.DBObjectId;
import ch.agent.crnickl.api.Database;
import ch.agent.crnickl.api.Schema;
import ch.agent.crnickl.api.Series;
import ch.agent.crnickl.api.Surrogate;
import ch.agent.crnickl.api.UpdatableChronicle;
import ch.agent.crnickl.api.UpdatableSeries;
import ch.agent.crnickl.impl.ChronicleUpdatePolicy;
import ch.agent.crnickl.impl.Permission;

import com.mongodb.BasicDBObject;
import com.mongodb.WriteConcern;

/**
 * A stateless object with methods providing write access to chronicles and
 * series.
 * To keep things as simple as possible in a first version,
 * chronicles and series values are stored separately, as in relational 
 * implementations. Changing this would require a non-negligible part of the
 * implementation.
 * 
 */
public class WriteMethodsForChroniclesAndSeries extends	ReadMethodsForChroniclesAndSeries {

	public WriteMethodsForChroniclesAndSeries() {
	}

	/**
	 * Create a chronicle in the database.
	 * Throw an exception if the operation cannot be done.
	 * 
	 * @param chronicle a chronicle
	 * @throws T2DBException
	 */
	public void createChronicle(Chronicle chronicle) throws T2DBException {
		createChronicle(chronicle, false);
	}
	private void createChronicle(Chronicle chronicle, boolean undoDelete) throws T2DBException {
		Surrogate surrogate = null;
		Throwable cause = null;
		try {
			String name = chronicle.getName(false);
			chronicle.getSurrogate().getDatabase().getNamingPolicy().checkSimpleName(name, false); // defensive
			String description = chronicle.getDescription(false);
			if (description == null)
				description = "";
			Chronicle collection = chronicle.getCollection();
			Schema schema = chronicle.getSchema(false);
			if (collection != null) {
				check(Permission.MODIFY, collection);
				if (schema != null && schema.equals(collection.getSchema(true)))
					schema = null; // don't repeat yourself
			}
			if (schema != null)
				check(Permission.READ, schema);
			DBObjectId ox = insert(chronicle);
			surrogate = makeSurrogate(chronicle, ox);
		} catch (Exception e) {
			cause = e;
		} finally {
		}
		if (surrogate == null || cause != null)
			throw T2DBMsg.exception(cause, E.E40109, chronicle.getName(true));
		if (!undoDelete)
			chronicle.getSurrogate().upgrade(surrogate);
	}
	
	/**
	 * Delete a chronicle from the database. Also delete its attribute values
	 * and possibly other dependent objects. The chronicle update policy is
	 * supposed to forbid deleting when there are dependent chronicles or
	 * series, but to allow cascading delete of attribute values. Throw an
	 * exception if the operation cannot be done.
	 * 
	 * @param chronicle a chronicle
	 * @param policy a chronicle updating policy
	 * @throws T2DBException
	 */
	public void deleteChronicle(UpdatableChronicle chronicle, ChronicleUpdatePolicy policy) throws T2DBException {
		boolean done = false;
		Throwable cause = null;
		Surrogate s = chronicle.getSurrogate();
		MongoDatabase db = (MongoDatabase) s.getDatabase();
		try {
			check(Permission.MODIFY, chronicle);
			policy.willDelete(chronicle);
			Chronicle original = getChronicle(s);
			getMongoDB(s).getChronicles().remove(asQuery(s.getId()), WriteConcern.SAFE);
			db.sleep();
			try {
				policy.willDelete(chronicle);
			} catch (T2DBException e) {
				// Oops! referential integrity broken!
				createChronicle(original, true);
				throw e;
			}
			deleteAttributes(chronicle);
			done = true;
		} catch (Exception e) {
			cause = e;
		} finally {
		}
		if (!done || cause != null) {
			throw T2DBMsg.exception(cause, E.E40110, chronicle.getName(true));
		}
	}
	
	/**
	 * Update a chronicle. Currently only name and description can be updated.
	 * The schema and the parent chronicle cannot be updated. Throw an exception
	 * if the operation cannot be done.
	 * 
	 * @param chronicle
	 *            a chronicle
	 * @param policy
	 *            a chronicle updating policy
	 * @throws T2DBException
	 */
	public void updateChronicle(UpdatableChronicle chronicle, ChronicleUpdatePolicy policy) throws T2DBException {
		String name = chronicle.getName(false);
		String description = chronicle.getDescription(false);
		if (description == null)
			description = "";
		try {
			check(Permission.MODIFY, chronicle);
			policy.willUpdate(chronicle);
			getMongoDB(chronicle.getSurrogate()).getChronicles().update(
					mongoObject(MongoDatabase.FLD_ID, getId(chronicle)), 
					mongoObject(
							MongoDatabase.FLD_CHRON_NAME, name, 
							MongoDatabase.FLD_CHRON_DESC, description),
					true, false);
		} catch (Exception e) {
			throw T2DBMsg.exception(E.E40111, chronicle.getName(true));
		}
	}
	
	/**
	 * Update a chronicle attribute.
	 * Throw an exception if the operation cannot be done.
	 * 
	 * @param chronicle a chronicle
	 * @param def an attribute definition
	 * @param value a value 
	 * @param description a string
	 * @throws T2DBException
	 */
	public void updateAttribute(UpdatableChronicle chronicle, AttributeDefinition def, String value, String description) throws T2DBException {
		try {
			check(Permission.MODIFY, chronicle);
			getMongoDB(chronicle.getSurrogate()).getAttributes().update(
					mongoObject(MongoDatabase.FLD_ATTR_CHRON, getId(chronicle), 
							MongoDatabase.FLD_ATTR_PROP, getId(def.getProperty())), 
					mongoObject(
							MongoDatabase.FLD_ATTR_CHRON, getId(chronicle), 
							MongoDatabase.FLD_ATTR_PROP, getId(def.getProperty()),
							MongoDatabase.FLD_ATTR_VALUE, value,
							MongoDatabase.FLD_ATTR_DESC, description),
					true, false);
		} catch (Exception e) {
			throw T2DBMsg.exception(E.E40112, chronicle.getName(true), def.getNumber());
		}
	}
	
	/**
	 * Delete an attribute value from a chronicle.
	 * Throw an exception if the operation cannot be done.
	 * 
	 * @param chronicle a chronicle
	 * @param def an attribute definition
	 * @throws T2DBException
	 */
	public void deleteAttribute(Chronicle chronicle, AttributeDefinition def) throws T2DBException {
		try {
			check(Permission.MODIFY, chronicle);
			getMongoDB(chronicle.getSurrogate()).getAttributes().remove(
					mongoObject(MongoDatabase.FLD_ATTR_CHRON, getId(chronicle), 
							MongoDatabase.FLD_ATTR_PROP, getId(def.getProperty())), 
							WriteConcern.SAFE);
		} catch (Exception e) {
			throw T2DBMsg.exception(E.E40114, chronicle.getName(true), def.getNumber());
		}
	}
	
	/**
	 * Delete all attribute values of a chronicle.
	 * Return the list of attributes, in case they need to be recreated.
	 * 
	 * @param chronicle
	 * @throws T2DBException
	 */
	private void deleteAttributes(Chronicle chronicle) throws T2DBException {
		Surrogate s = chronicle.getSurrogate();
		Database db = s.getDatabase();
		try {
			getMongoDB(db).getAttributes().remove(
					mongoObject(MongoDatabase.FLD_ATTR_CHRON,getId(s)));
		} catch (Exception e) {
			throw T2DBMsg.exception(e, E.E40124, chronicle.getName(true));
		}
	}

	/**
	 * Create an empty series. Throw an exception if the operation cannot be
	 * done.
	 * 
	 * @param series
	 *            a series
	 * @throws T2DBException
	 */
	public void createSeries(Series series) throws T2DBException {
		createSeries(series, false);
	}
	private void createSeries(Series series, boolean undoDelete) throws T2DBException {
		Surrogate surrogate = null;
		Throwable cause = null;
		try {
			check(Permission.MODIFY, series.getChronicle());
			DBObjectId ox = insert(series);
			surrogate = makeSurrogate(series, ox);
		} catch (Exception e) {
			cause = e;
		} finally {
		}
		if (surrogate == null || cause != null)
			throw T2DBMsg.exception(cause, E.E50111, series.getName(true));
		if (!undoDelete)
			series.getSurrogate().upgrade(surrogate);
	}
	
	/**
	 * Delete a series. The policy typically forbids to delete a series which is
	 * not empty. Throw an exception if the operation cannot be done.
	 * 
	 * @param series
	 *            a series
	 * @param policy
	 *            a chronicle update policy
	 * @throws T2DBException
	 */
	public void deleteSeries(UpdatableSeries series, ChronicleUpdatePolicy policy) throws T2DBException {
		boolean done = false;
		Throwable cause = null;
		Surrogate s = series.getSurrogate();
		MongoDatabase db = (MongoDatabase) s.getDatabase();
		try {
			check(Permission.MODIFY, series);
			policy.willDelete(series);
			done = policy.deleteSeries(series);
			Series original = db.getReadMethodsForChronicleAndSeries().getSeries(s);
			getMongoDB(db).getSeries().remove(asQuery(s.getId()), WriteConcern.SAFE);
			db.sleep();
			try {
				policy.willDelete(series);
			} catch (T2DBException e) {
				// Oops! referential integrity broken!
				createSeries(original, true);
				throw e;
			}
			done = true;
		} catch (Exception e) {
			cause = e;
		}
		if (!done || cause != null)
			throw T2DBMsg.exception(cause, E.E50112, series.getName(true));
	}
	
	private DBObjectId insert(Chronicle chronicle) throws T2DBException {
		com.mongodb.BasicDBObject bo = new BasicDBObject();
		Surrogate s = chronicle.getSurrogate();
		if (!s.inConstruction()) 
			bo.put(MongoDatabase.FLD_ID, getId(chronicle)); // use case: re-creating
		bo.put(MongoDatabase.FLD_CHRON_NAME, chronicle.getName(false));
		bo.put(MongoDatabase.FLD_CHRON_DESC, chronicle.getDescription(false));
		bo.put(MongoDatabase.FLD_CHRON_PARENT, getIdOrZero(chronicle.getCollection()));
		bo.put(MongoDatabase.FLD_CHRON_SCHEMA, getIdOrZero(chronicle.getSchema(false)));
		getMongoDB(s).getChronicles().insert(bo);
		ObjectId ox = getObjectId(bo);	
		return new MongoDBObjectId(ox);
	}

	private DBObjectId insert(Series series) throws T2DBException {
		com.mongodb.BasicDBObject bo = new BasicDBObject();
		Surrogate s = series.getSurrogate();
		if (!s.inConstruction()) 
			bo.put(MongoDatabase.FLD_ID, getId(series)); // use case: re-creating
		bo.put(MongoDatabase.FLD_SER_CHRON, getIdOrZero(series.getChronicle()));
		bo.put(MongoDatabase.FLD_SER_NUM, series.getNumber());
		bo.put(MongoDatabase.FLD_SER_FIRST, 1);
		bo.put(MongoDatabase.FLD_SER_LAST, 0);
		bo.put(MongoDatabase.FLD_SER_VALUES, new HashMap());
		getMongoDB(s).getSeries().insert(bo);
		ObjectId ox = getObjectId(bo);	
		return new MongoDBObjectId(ox);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy