Java tutorial
/* * Copyright 2013 Keith Flanagan * * 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 uk.ac.ncl.aries.entanglement.graph; import com.mongodb.BasicDBObject; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; import com.mongodb.Mongo; import com.mongodb.WriteResult; import com.torrenttamer.mongodb.dbobject.DbObjectMarshaller; import com.torrenttamer.mongodb.dbobject.KeyExtractingIterable; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import uk.ac.ncl.aries.entanglement.ObjectMarshallerFactory; /** * * @author Keith Flanagan */ abstract public class AbstractGraphEntityDAO implements GraphEntityDAO { private static final Logger logger = Logger.getLogger(AbstractGraphEntityDAO.class.getName()); /* * Indexes UID */ private static final DBObject IDX_UID = new BasicDBObject(FIELD_UID, 1); /* * Indexes entity type, followed by entity name */ private static final DBObject IDX_TYPE_AND_NAME = new BasicDBObject(FIELD_TYPE, 1).append(FIELD_NAME, 1); protected final Mongo m; protected final DB db; protected InsertMode insertModeHint; // private final JsonUtils json; protected final DbObjectMarshaller marshaller; ////////// DEBUG / TEST - Performance info stuff private static final int PRINT_PERF_INFO_EVERY = 100000; private long timestampOfFirstInsert = -1; private long timestampOfLastPerformanceMessage = -1; private boolean printPeriodicPerformanceInfo; private int insertCount; ////////// DEBUG / TEST - Performance info stuff (end) // private final String graphUid; // private final String branchUid; /** * The MongoDB collection used for storing data about this graph checkout - * we make the assumption that there is one graph checkout per collection. */ protected final DBCollection col; /** * * @param classLoader a custom classloader may be required here in order for * a <code>DbObjectMarshaller</code> to correctly deserialize objects. * * TODO If we we only use the marshaller for serialization, then we probably * don't need this parameter. * @param m * @param db * @param col */ public AbstractGraphEntityDAO(ClassLoader classLoader, Mongo m, DB db, DBCollection col) { this.m = m; this.db = db; this.col = col; marshaller = ObjectMarshallerFactory.create(classLoader); printPeriodicPerformanceInfo = true; insertCount = 0; insertModeHint = InsertMode.INSERT_CONSISTENCY; //Make sure indexes exist col.ensureIndex(IDX_UID); col.ensureIndex(IDX_TYPE_AND_NAME); } @Override public InsertMode getInsertModeHint() { return insertModeHint; } @Override public void setInsertModeHint(InsertMode insertMode) { this.insertModeHint = insertMode; } @Override public DBCollection getCollection() { return col; } @Override public void store(BasicDBObject item) throws GraphModelException { try { // logger.log(Level.INFO, "Storing node: {0}", node); if (insertModeHint == InsertMode.INSERT_CONSISTENCY) { String uid = item.getString(FIELD_UID); String name = item.getString(FIELD_NAME); String type = item.getString(FIELD_TYPE); // logger.info("Running in consistency mode"); if (existsByUid(uid)) { throw new GraphModelException( "Failed to store item - an entity with this unique ID already exists: " + uid); } if (name != null && existsByName(type, name)) { throw new GraphModelException( "Failed to store item - an entity with the same 'well known' name already exists: " + name); } } col.insert(item); /////// DEBUG (Performance info) if (printPeriodicPerformanceInfo) { insertCount++; if (timestampOfLastPerformanceMessage < 0) { //First ever insert long now = System.currentTimeMillis(); timestampOfLastPerformanceMessage = now; timestampOfFirstInsert = now; return; } if (insertCount % PRINT_PERF_INFO_EVERY == 0) { long now = System.currentTimeMillis(); double secondsPerBlock = (now - timestampOfLastPerformanceMessage); secondsPerBlock = secondsPerBlock / 1000; double totalSeconds = (now - timestampOfFirstInsert); totalSeconds = totalSeconds / 1000; logger.log(Level.INFO, "Inserted a total of\t{0}\t" + getClass().getSimpleName() + " documents. " + "Total time\t{1}\t seconds. Seconds since last block: {2}", new Object[] { insertCount, totalSeconds, secondsPerBlock }); timestampOfLastPerformanceMessage = now; } } /////// DEBUG (Performance info) (end) } catch (Exception e) { throw new GraphModelException("Failed to store item: " + item, e); } } @Override public void setPropertyByUid(String uid, String propertyName, Object propertyValue) throws GraphModelException { // logger.log(Level.INFO, "Storing edge: {0}", edge); DBObject criteria = null; DBObject fieldsToReturn = null; DBObject sort = null; DBObject update = null; boolean remove = false; boolean returnNew = false; boolean upsert = false; try { criteria = new BasicDBObject(FIELD_UID, uid); fieldsToReturn = new BasicDBObject(); sort = new BasicDBObject(); DBObject valDbObj = marshaller.serialize(propertyValue); //Use of '$' operators causes an update, rather than full doc replacement update = new BasicDBObject("$set", new BasicDBObject(propertyName, valDbObj)); //Perform atomic update of a single document col.findAndModify(criteria, fieldsToReturn, sort, remove, update, returnNew, upsert); } catch (Exception e) { throw new GraphModelException("Failed to store item: " + propertyName + " on: " + uid, e); } } @Override public void setPropertyByName(String entityType, String entityName, String propertyName, Object propertyValue) throws GraphModelException { // logger.log(Level.INFO, "Storing edge: {0}", edge); DBObject criteria = null; DBObject fieldsToReturn = null; DBObject sort = null; DBObject update = null; boolean remove = false; boolean returnNew = false; boolean upsert = false; try { criteria = new BasicDBObject(FIELD_TYPE, entityType).append(FIELD_NAME, entityName); fieldsToReturn = new BasicDBObject(); sort = new BasicDBObject(); DBObject valDbObj = marshaller.serialize(propertyValue); //Use of '$' operators causes an update, rather than full doc replacement update = new BasicDBObject("$set", new BasicDBObject(propertyName, valDbObj)); //Perform atomic update of a single document col.findAndModify(criteria, fieldsToReturn, sort, remove, update, returnNew, upsert); } catch (Exception e) { throw new GraphModelException("Failed to store item: " + propertyName + " on entity with type: " + entityType + ", name: " + entityType, e); } } @Override public String lookupUniqueIdForName(String type, String name) throws GraphModelException { DBObject query = null; DBObject fields = null; try { query = new BasicDBObject(); query.put(FIELD_TYPE, type); query.put(FIELD_NAME, name); fields = new BasicDBObject(); fields.put(FIELD_UID, 1); DBObject result = col.findOne(query); if (result == null) { //There is no node with this name return null; } String nodeUniqueId = (String) result.get(FIELD_UID); return nodeUniqueId; } catch (Exception e) { throw new GraphModelException("Failed to perform database operation:\n" + "Query: " + query, e); } } @Override public DBObject getByUid(String nodeUid) throws GraphModelException { DBObject query = null; try { // logger.log(Level.INFO, "Getting node by UID: {0}", nodeUid); query = new BasicDBObject(); query.put(FIELD_UID, nodeUid); DBObject obj = col.findOne(query); if (obj == null) { return null; } return obj; } catch (Exception e) { throw new GraphModelException("Failed to perform database operation: \n" + "Query: " + query, e); } } @Override public DBObject getByName(String type, String name) throws GraphModelException { DBObject query = null; try { query = new BasicDBObject(); query.put(FIELD_TYPE, type); query.put(FIELD_NAME, name); DBObject nodeObj = col.findOne(query); if (nodeObj == null) { return null; } return nodeObj; } catch (Exception e) { throw new GraphModelException("Failed to perform database operation: \n" + "Query: " + query, e); } } @Override public boolean existsByUid(String uniqueId) throws GraphModelException { DBObject query = null; try { query = new BasicDBObject(); query.put(FIELD_UID, uniqueId); long count = col.count(query); if (count > 1) { throw new GraphModelException("Unique ID: " + uniqueId + " should be unique, but we found: " + count + " instances with that name!"); } return count == 1; } catch (Exception e) { throw new GraphModelException("Failed to perform database operation: \n" + "Query: " + query, e); } } @Override public boolean existsByName(String entityType, String entityName) throws GraphModelException { DBObject query = null; try { query = new BasicDBObject(); query.put(FIELD_TYPE, entityType); query.put(FIELD_NAME, entityName); long count = col.count(query); if (count > 1) { throw new GraphModelException("Type: " + entityType + ", Name: " + entityName + " should be unique, but we found: " + count + " instances with that name!"); } return count == 1; } catch (Exception e) { throw new GraphModelException("Failed to perform database operation:\n" + "Query: " + query, e); } } @Override public DBObject deleteByUid(String uid) throws GraphModelException { DBObject query = null; try { // logger.log(Level.INFO, "Deleting node by UID: {0}", nodeUid); DBObject toDelete = getByUid(uid); if (toDelete == null) { throw new GraphModelException("Attempted a delete operation, but no such entity exists: " + uid); } /* * Below code is commented for now, but we need to implement this at * some point for consistency reasons. */ // Check that this node doesn't connect to any others // if (!toDelete.getOutgoingEdges().isEmpty()) // { // throw new GraphModelException( // "Attempted to delete node: "+nodeUid // + ". However, the node contains outgoing edges to other nodes." // + " Delete these first before attempting to remote this node."); // } // Check that this node is not connected to by others // if (!toDelete.getIncomingEdgeIds().isEmpty()) // { // throw new GraphModelException( // "Attempted to delete node: "+nodeUid // + ". However, the node contains incoming edges from other nodes." // + " Delete these first before attempting to remote this node."); // } //Delete the specified object query = new BasicDBObject(); query.put(FIELD_UID, uid); WriteResult result = col.remove(query); // logger.log(Level.INFO, "WriteResult: {0}", result.toString()); return toDelete; } catch (Exception e) { throw new GraphModelException("Failed to perform database operation: \n" + "Query: " + query, e); } } @Override public DBCursor iterateAll() throws GraphModelException { DBObject query = null; try { //Empty 'query' selects all documents (nodes in this case) query = new BasicDBObject(); final DBCursor cursor = col.find(query); return cursor; } catch (Exception e) { throw new GraphModelException("Failed to perform database operation: \n" + "Query: " + query, e); } } @Override public List<String> listTypes() throws GraphModelException { try { List<String> types = (List<String>) col.distinct(FIELD_TYPE); return types; } catch (Exception e) { throw new GraphModelException("Failed to perform database operation\n", e); } } @Override public Iterable<DBObject> iterateByType(String typeName) throws GraphModelException { DBObject query = null; try { // logger.log(Level.INFO, "Getting node(s) by type: {0}", typeName); query = new BasicDBObject(); query.put(FIELD_TYPE, typeName); // logger.log(Level.INFO, "Generated query: {0}", query); DBCursor cursor = col.find(query); return cursor; } catch (Exception e) { throw new GraphModelException("Failed to perform database operation to find nodes of type: " + typeName + "\n" + "Query was: " + query, e); } } @Override public Iterable<String> iterateIdsByType(String typeName, int offset, int limit) throws GraphModelException { DBObject query = null; try { query = new BasicDBObject(); query.put(FIELD_TYPE, typeName); DBObject keys = new BasicDBObject(FIELD_UID, 1); DBCursor cursor = col.find(query, keys, offset, limit); return new KeyExtractingIterable<>(cursor, FIELD_UID, String.class); } catch (Exception e) { throw new GraphModelException( "Failed to perform database operation. Type was: " + typeName + "\n" + "Query was: " + query, e); } } @Override public Iterable<String> iterateNamesByType(String typeName, int offset, int limit) throws GraphModelException { DBObject query = null; try { query = new BasicDBObject(); query.put(FIELD_TYPE, typeName); DBObject keys = new BasicDBObject(FIELD_NAME, 1); DBCursor cursor = col.find(query, keys, offset, limit); return new KeyExtractingIterable<>(cursor, FIELD_NAME, String.class); } catch (Exception e) { throw new GraphModelException( "Failed to perform database operation. Type was: " + typeName + "\n" + "Query was: " + query, e); } } @Override public long countByType(String typeName) throws GraphModelException { DBObject query = null; try { query = new BasicDBObject(); query.put(FIELD_TYPE, typeName); return col.count(query); } catch (Exception e) { throw new GraphModelException("Failed to perform database operation\n" + "Query was: " + query, e); } } @Override public long count() throws GraphModelException { try { return col.count(); } catch (Exception e) { throw new GraphModelException("Failed to perform database operation", e); } } }