Java tutorial
/******************************************************************************* * * Copyright 2012 Impetus Infotech. * * * * 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.impetus.client.mongodb; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.Lob; import javax.persistence.PersistenceException; import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.EmbeddableType; import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.Metamodel; import org.bson.types.ObjectId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.io.ByteStreams; import com.impetus.client.mongodb.utils.MongoDBUtils; import com.impetus.kundera.KunderaException; import com.impetus.kundera.db.RelationHolder; import com.impetus.kundera.metadata.KunderaMetadataManager; import com.impetus.kundera.metadata.model.EntityMetadata; import com.impetus.kundera.metadata.model.MetamodelImpl; import com.impetus.kundera.metadata.model.attributes.AbstractAttribute; import com.impetus.kundera.metadata.model.type.AbstractManagedType; import com.impetus.kundera.persistence.EntityManagerFactoryImpl.KunderaMetadata; import com.impetus.kundera.property.PropertyAccessException; import com.impetus.kundera.property.PropertyAccessorHelper; import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; import com.mongodb.gridfs.GridFS; import com.mongodb.gridfs.GridFSDBFile; import com.mongodb.gridfs.GridFSInputFile; /** * Provides utility methods for handling data held in MongoDB. * * @author amresh.singh */ public final class DefaultMongoDBDataHandler implements MongoDBDataHandler { /** The log. */ private static Logger log = LoggerFactory.getLogger(DefaultMongoDBDataHandler.class); /** * Gets the entity from document. * * @param entityClass * the entity class * @param m * the m * @param document * the document * @param relations * the relations * @return the entity from document */ public Map<String, Object> getEntityFromDocument(Class<?> entityClass, Object entity, EntityMetadata m, DBObject document, List<String> relations, Map<String, Object> relationValue, final KunderaMetadata kunderaMetadata) { // Map to hold property-name=>foreign-entity relations try { // Populate primary key column Object rowKey = document.get("_id"); Class<?> rowKeyValueClass = rowKey.getClass(); Class<?> idClass = null; MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata() .getMetamodel(m.getPersistenceUnit()); idClass = m.getIdAttribute().getJavaType(); rowKey = MongoDBUtils.populateValue(rowKey, idClass); if (metaModel.isEmbeddable(m.getIdAttribute().getBindableJavaType())) { EmbeddableType embeddable = metaModel.embeddable(m.getIdAttribute().getBindableJavaType()); Iterator<Attribute> iter = embeddable.getAttributes().iterator(); Object compoundKey = m.getIdAttribute().getBindableJavaType().newInstance(); while (iter.hasNext()) { AbstractAttribute compositeAttrib = (AbstractAttribute) iter.next(); Object value = ((BasicDBObject) rowKey).get(compositeAttrib.getJPAColumnName()); PropertyAccessorHelper.set(compoundKey, (Field) compositeAttrib.getJavaMember(), value); } PropertyAccessorHelper.setId(entity, m, compoundKey); } else { rowKey = MongoDBUtils.getTranslatedObject(rowKey, rowKeyValueClass, idClass); PropertyAccessorHelper.setId(entity, m, rowKey); } // Populate entity columns EntityType entityType = metaModel.entity(entityClass); Set<Attribute> columns = entityType.getAttributes(); for (Attribute column : columns) { if (!column.equals(m.getIdAttribute())) { String jpaColumnName = ((AbstractAttribute) column).getJPAColumnName(); Class javaType = ((AbstractAttribute) column).getBindableJavaType(); if (metaModel.isEmbeddable(javaType)) { onViaEmbeddable(column, entity, metaModel, document); } else if (!column.isAssociation()) { DocumentObjectMapper.setFieldValue(document, entity, column, false); } else if (relations != null) { if (relationValue == null) { relationValue = new HashMap<String, Object>(); } if (relations.contains(jpaColumnName) && !jpaColumnName .equals(((AbstractAttribute) m.getIdAttribute()).getJPAColumnName())) { Object colValue = document.get(jpaColumnName); if (colValue != null) { String colFieldName = m.getFieldName(jpaColumnName); EntityMetadata relationMetadata = KunderaMetadataManager.getEntityMetadata( kunderaMetadata, ((AbstractAttribute) entityType.getAttribute(colFieldName)) .getBindableJavaType()); colValue = MongoDBUtils.getTranslatedObject(colValue, colValue.getClass(), relationMetadata.getIdAttribute().getJavaType()); } relationValue.put(jpaColumnName, colValue); } } } } return relationValue; } catch (InstantiationException e) { log.error("Error while instantiating " + entityClass + ", Caused by: ", e); return relationValue; } catch (IllegalAccessException e) { log.error("Error while Getting entity from Document, Caused by: ", e); return relationValue; } catch (PropertyAccessException e) { log.error("Error while Getting entity from Document, Caused by: ", e); return relationValue; } } /** * Gets the entity from GFSDBFile. * * @param entityClazz * the entity clazz * @param entity * the entity * @param m * the m * @param outputFile * the output file * @param kunderaMetadata * the kundera metadata * @return the entity from GFSDBFile */ public Object getEntityFromGFSDBFile(Class<?> entityClazz, Object entity, EntityMetadata m, GridFSDBFile outputFile, KunderaMetadata kunderaMetadata) { MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata() .getMetamodel(m.getPersistenceUnit()); String id = ((AbstractAttribute) m.getIdAttribute()).getJPAColumnName(); Object rowKey = ((DBObject) outputFile.get(MongoDBUtils.METADATA)).get(id); Class<?> rowKeyValueClass = rowKey.getClass(); Class<?> idClass = m.getIdAttribute().getJavaType(); rowKey = MongoDBUtils.populateValue(rowKey, idClass); rowKey = MongoDBUtils.getTranslatedObject(rowKey, rowKeyValueClass, idClass); PropertyAccessorHelper.setId(entity, m, rowKey); EntityType entityType = metaModel.entity(entityClazz); Set<Attribute> columns = entityType.getAttributes(); for (Attribute column : columns) { boolean isLob = ((Field) column.getJavaMember()).getAnnotation(Lob.class) != null; if (isLob) { if (column.getJavaType().isAssignableFrom(byte[].class)) { InputStream is = outputFile.getInputStream(); try { PropertyAccessorHelper.set(entity, (Field) column.getJavaMember(), ByteStreams.toByteArray(is)); } catch (IOException e) { log.error("Error while converting inputstream from GridFSDBFile to byte array, Caused by: ", e); throw new KunderaException( "Error while converting inputstream from GridFSDBFile to byte array, Caused by: ", e); } } } else if (!column.equals(m.getIdAttribute())) DocumentObjectMapper.setFieldValue(outputFile, entity, column, true); } return entity; } /** * Gets the document from entity. * * @param m * the m * @param entity * the entity * @param relations * the relations * @return the document from entity * @throws PropertyAccessException * the property access exception */ public Map<String, DBObject> getDocumentFromEntity(EntityMetadata m, Object entity, List<RelationHolder> relations, final KunderaMetadata kunderaMetadata) throws PropertyAccessException { Map<String, DBObject> dbObjects = new HashMap<String, DBObject>(); MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata() .getMetamodel(m.getPersistenceUnit()); EntityType entityType = metaModel.entity(m.getEntityClazz()); // Populate Row Key Object id = PropertyAccessorHelper.getId(entity, m); DBObject dbObj = MongoDBUtils.getDBObject(m, m.getTableName(), dbObjects, metaModel, id); // dbObjects.put(m.getTableName(), dbObj); // Populate columns Set<Attribute> columns = entityType.getAttributes(); for (Attribute column : columns) { String tableName = ((AbstractAttribute) column).getTableName() != null ? ((AbstractAttribute) column).getTableName() : m.getTableName(); dbObj = MongoDBUtils.getDBObject(m, tableName, dbObjects, metaModel, id); if (!column.equals(m.getIdAttribute())) { try { Class javaType = ((AbstractAttribute) column).getBindableJavaType(); if (metaModel.isEmbeddable(javaType)) { Map<String, DBObject> embeddedObjects = onEmbeddable(column, entity, metaModel, dbObj, m.getTableName()); for (String documentName : embeddedObjects.keySet()) { DBObject db = dbObjects.get(documentName); if (db == null) { db = MongoDBUtils.getDBObject(m, documentName, dbObjects, metaModel, id); } db.put(((AbstractAttribute) column).getJPAColumnName(), embeddedObjects.get(documentName)); dbObjects.put(documentName, db); } } else if (!column.isAssociation()) { DocumentObjectMapper.extractFieldValue(entity, dbObj, column); } } catch (PropertyAccessException paex) { log.error("Can't access property " + column.getName()); } } // dbObjects.put(tableName, dbObj); } if (relations != null) { dbObj = dbObjects.get(m.getTableName()); for (RelationHolder rh : relations) { dbObj.put(rh.getRelationName(), MongoDBUtils.populateValue(rh.getRelationValue(), rh.getRelationValue().getClass())); } // dbObjects.put(m.getTableName(), dbObj); } if (((AbstractManagedType) entityType).isInherited()) { dbObj = dbObjects.get(m.getTableName()); String discrColumn = ((AbstractManagedType) entityType).getDiscriminatorColumn(); String discrValue = ((AbstractManagedType) entityType).getDiscriminatorValue(); // No need to check for empty or blank, as considering it as valid // name for nosql! if (discrColumn != null && discrValue != null) { dbObj.put(discrColumn, discrValue); } // dbObjects.put(m.getTableName(), dbObj); } return dbObjects; } /** * Gets the GFSInputFile from entity. * * @param gfs * the gfs * @param m * the m * @param entity * the entity * @param kunderaMetadata * the kundera metadata * @return the GFS iuput file from entity */ public GridFSInputFile getGFSInputFileFromEntity(GridFS gfs, EntityMetadata m, Object entity, KunderaMetadata kunderaMetadata, boolean isUpdate) { MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata() .getMetamodel(m.getPersistenceUnit()); EntityType entityType = metaModel.entity(m.getEntityClazz()); GridFSInputFile gridFSInputFile = null; DBObject gfsMetadata = new BasicDBObject(); Set<Attribute> columns = entityType.getAttributes(); for (Attribute column : columns) { boolean isLob = ((Field) column.getJavaMember()).getAnnotation(Lob.class) != null; if (isLob) { gridFSInputFile = createGFSInputFile(gfs, entity, (Field) column.getJavaMember()); gridFSInputFile.setFilename(column.getName()); } else { if (isUpdate && column.getName().equals(m.getIdAttribute().getName())) { gfsMetadata.put(((AbstractAttribute) column).getJPAColumnName(), new ObjectId()); } else DocumentObjectMapper.extractFieldValue(entity, gfsMetadata, column); } } gridFSInputFile.setMetaData(gfsMetadata); return gridFSInputFile; } /** * Creates the GFS Input file. * * @param gfs * the gfs * @param entity * the entity * @param f * the f * @return the grid fs input file */ private GridFSInputFile createGFSInputFile(GridFS gfs, Object entity, Field f) { Object obj = PropertyAccessorHelper.getObject(entity, f); GridFSInputFile gridFSInputFile = null; if (f.getType().isAssignableFrom(byte[].class)) gridFSInputFile = gfs.createFile((byte[]) obj); else if (f.getType().isAssignableFrom(File.class)) { try { gridFSInputFile = gfs.createFile((File) obj); } catch (IOException e) { log.error("Error while creating GridFS file for \"" + f.getName() + "\". Caused by: ", e); throw new KunderaException( "Error while creating GridFS file for \"" + f.getName() + "\". Caused by: ", e); } } else new UnsupportedOperationException(f.getType().getSimpleName() + " is unsupported Lob object"); return gridFSInputFile; } /** * Retrieves A collection of embedded object within a document that match a * criteria specified in <code>query</code> TODO: This code requires a * serious overhawl. Currently it assumes that user query is in the form * "Select alias.columnName from EntityName alias". However, correct query * to be supported is * "Select alias.superColumnName.columnName from EntityName alias" * * @param dbCollection * the db collection * @param m * the m * @param documentName * the document name * @param mongoQuery * the mongo query * @param result * the result * @param orderBy * the order by * @param maxResult * @return the embedded object list * @throws PropertyAccessException * the property access exception * @throws IllegalAccessException * @throws InstantiationException */ public List getEmbeddedObjectList(DBCollection dbCollection, EntityMetadata m, String documentName, BasicDBObject mongoQuery, String result, BasicDBObject orderBy, int maxResult, int firstResult, BasicDBObject keys, final KunderaMetadata kunderaMetadata) throws PropertyAccessException, InstantiationException, IllegalAccessException { List list = new ArrayList();// List of embedded object to be returned // Specified after entity alias in query String columnName = result; // Something user didn't specify and we have to derive // TODO: User must specify this in query and remove this logic once // query format is changed String enclosingDocumentName = null; MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata() .getMetamodel(m.getPersistenceUnit()); EntityType entityType = metaModel.entity(m.getEntityClazz()); EmbeddableType superColumn = null; Set<Attribute> columns = null; Attribute attrib = null; try { attrib = entityType.getAttribute(columnName); Map<String, EmbeddableType> embeddables = metaModel.getEmbeddables(m.getEntityClazz()); for (String key : embeddables.keySet()) { superColumn = embeddables.get(key); columns = superColumn.getAttributes(); for (Attribute column : columns) { if (((AbstractAttribute) column).getJPAColumnName().equals(columnName)) { enclosingDocumentName = key; break; } } } } catch (IllegalArgumentException iax) { if (log.isWarnEnabled()) { log.warn("No column found for: " + columnName); } } // Query for fetching entities based on user specified criteria DBCursor cursor = orderBy != null ? dbCollection.find(mongoQuery, keys).sort(orderBy) : dbCollection.find(mongoQuery, keys).limit(maxResult).skip(firstResult); if (superColumn != null) { Field superColumnField = (Field) attrib.getJavaMember(); while (cursor.hasNext()) { DBObject fetchedDocument = cursor.next(); Object embeddedDocumentObject = fetchedDocument.get(superColumnField.getName()); if (embeddedDocumentObject != null) { if (embeddedDocumentObject instanceof BasicDBList) { Class embeddedObjectClass = PropertyAccessorHelper.getGenericClass(superColumnField); for (Object dbObj : (BasicDBList) embeddedDocumentObject) { Object obj = embeddedObjectClass.newInstance(); Object embeddedObject = new DocumentObjectMapper().getObjectFromDocument(metaModel, (BasicDBObject) dbObj, superColumn.getAttributes(), obj); Object fieldValue = PropertyAccessorHelper.getObject(embeddedObject, columnName); } } else if (embeddedDocumentObject instanceof BasicDBObject) { Object obj = superColumn.getJavaType().newInstance(); Object embeddedObject = DocumentObjectMapper.getObjectFromDocument(metaModel, (BasicDBObject) embeddedDocumentObject, superColumn.getAttributes(), obj); list.add(embeddedObject); } else { throw new PersistenceException("Can't retrieve embedded object from MONGODB document coz " + "it wasn't stored as BasicDBObject, possible problem in format."); } } } } return list; } /** * @param entityType * @param column * @param m * @param entity */ Map<String, DBObject> onEmbeddable(Attribute column, Object entity, Metamodel metaModel, DBObject dbObj, String tableName) { EmbeddableType embeddableType = metaModel.embeddable(((AbstractAttribute) column).getBindableJavaType()); Object embeddedObject = PropertyAccessorHelper.getObject(entity, (Field) column.getJavaMember()); Map<String, DBObject> embeddedObjects = new HashMap<String, DBObject>(); if (embeddedObject != null) { if (column.isCollection()) { Collection embeddedCollection = (Collection) embeddedObject; // means it is case of element collection dbObj.put(((AbstractAttribute) column).getJPAColumnName(), DocumentObjectMapper.getDocumentListFromCollection(metaModel, embeddedCollection, embeddableType.getAttributes(), tableName)); } else { embeddedObjects = DocumentObjectMapper.getDocumentFromObject(metaModel, embeddedObject, embeddableType.getAttributes(), tableName); dbObj.put(((AbstractAttribute) column).getJPAColumnName(), embeddedObjects.get(tableName)); } } return embeddedObjects; } /** * @param entityType * @param column * @param m * @param entity * @param embeddable * @param document * @throws IllegalAccessException * @throws InstantiationException */ void onViaEmbeddable(Attribute column, Object entity, Metamodel metamodel, DBObject document) throws InstantiationException, IllegalAccessException { EmbeddableType embeddable = metamodel.embeddable(((AbstractAttribute) column).getBindableJavaType()); Field embeddedField = (Field) column.getJavaMember(); Object embeddedDocumentObject = null; if (column.isCollection()) { Class embeddedObjectClass = PropertyAccessorHelper.getGenericClass(embeddedField); embeddedDocumentObject = document.get(((AbstractAttribute) column).getJPAColumnName()); if (embeddedDocumentObject != null) { Collection embeddedCollection = DocumentObjectMapper.getCollectionFromDocumentList(metamodel, (BasicDBList) embeddedDocumentObject, embeddedField.getType(), embeddedObjectClass, embeddable.getAttributes()); PropertyAccessorHelper.set(entity, embeddedField, embeddedCollection); } } else { Object obj = PropertyAccessorHelper.getObject(entity, (Field) column.getJavaMember()); if (obj == null) { obj = ((AbstractAttribute) column).getBindableJavaType().newInstance(); } embeddedDocumentObject = document.get(((AbstractAttribute) column).getJPAColumnName()); PropertyAccessorHelper.set(entity, embeddedField, DocumentObjectMapper.getObjectFromDocument(metamodel, (BasicDBObject) embeddedDocumentObject, embeddable.getAttributes(), obj)); } } /** * Gets the lob from GFS entity. * * @param gfs * the gfs * @param m * the m * @param entity * the entity * @param kunderaMetadata * the kundera metadata * @return the lob from gfs entity */ public Object getLobFromGFSEntity(GridFS gfs, EntityMetadata m, Object entity, KunderaMetadata kunderaMetadata) { MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata() .getMetamodel(m.getPersistenceUnit()); EntityType entityType = metaModel.entity(m.getEntityClazz()); Set<Attribute> columns = entityType.getAttributes(); for (Attribute column : columns) { boolean isLob = ((Field) column.getJavaMember()).getAnnotation(Lob.class) != null; if (isLob) { return PropertyAccessorHelper.getObject(entity, (Field) column.getJavaMember()); } } return null; } /** * Gets the metadata from GFS entity. * * @param gfs * the gfs * @param m * the m * @param entity * the entity * @param kunderaMetadata * the kundera metadata * @return the metadata from GFS entity */ public DBObject getMetadataFromGFSEntity(GridFS gfs, EntityMetadata m, Object entity, KunderaMetadata kunderaMetadata) { MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata() .getMetamodel(m.getPersistenceUnit()); EntityType entityType = metaModel.entity(m.getEntityClazz()); DBObject gfsMetadata = new BasicDBObject(); Set<Attribute> columns = entityType.getAttributes(); for (Attribute column : columns) { boolean isLob = ((Field) column.getJavaMember()).getAnnotation(Lob.class) != null; if (!isLob) { DocumentObjectMapper.extractFieldValue(entity, gfsMetadata, column); } } return gfsMetadata; } }