org.springframework.datastore.mapping.mongo.MongoDatastore.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.datastore.mapping.mongo.MongoDatastore.java

Source

/* Copyright (C) 2010 SpringSource
 *
 * 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 org.springframework.datastore.mapping.mongo;

import static org.springframework.datastore.mapping.config.utils.ConfigUtils.read;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.bson.types.ObjectId;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.DataAccessException;
import org.springframework.data.document.mongodb.DbCallback;
import org.springframework.data.document.mongodb.MongoFactoryBean;
import org.springframework.data.document.mongodb.MongoTemplate;
import org.springframework.datastore.mapping.core.AbstractDatastore;
import org.springframework.datastore.mapping.core.Session;
import org.springframework.datastore.mapping.document.config.DocumentMappingContext;
import org.springframework.datastore.mapping.model.ClassMapping;
import org.springframework.datastore.mapping.model.DatastoreConfigurationException;
import org.springframework.datastore.mapping.model.MappingContext;
import org.springframework.datastore.mapping.model.PersistentEntity;
import org.springframework.datastore.mapping.model.PersistentProperty;
import org.springframework.datastore.mapping.model.PropertyMapping;
import org.springframework.datastore.mapping.mongo.config.MongoAttribute;
import org.springframework.datastore.mapping.mongo.config.MongoCollection;
import org.springframework.datastore.mapping.mongo.config.MongoMappingContext;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
import com.mongodb.MongoOptions;
import com.mongodb.ServerAddress;
import com.mongodb.WriteConcern;

/**
 * A Datastore implementation for the Mongo document store
 *
 * @author Graeme Rocher
 * @since 1.0
 */
public class MongoDatastore extends AbstractDatastore implements InitializingBean, MappingContext.Listener {

    public static final String PASSWORD = "password";
    public static final String USERNAME = "username";
    public static final String MONGO_PORT = "port";
    public static final String MONGO_HOST = "host";

    private Mongo mongo;
    private MongoOptions mongoOptions = new MongoOptions();
    private Map<PersistentEntity, MongoTemplate> mongoTemplates = new ConcurrentHashMap<PersistentEntity, MongoTemplate>();

    /**
     * Constructs a MongoDatastore using the default database name of "test" and defaults for the host and port.
     * Typically used during testing. 
     */
    public MongoDatastore() {
        this(new MongoMappingContext("test"), Collections.<String, String>emptyMap());
    }

    /**
     * Constructs a MongoDatastore using the given MappingContext and connection details map.
     * 
     * @param mappingContext The MongoMappingContext
     * @param connectionDetails The connection details containing the {@link #MONGO_HOST} and {@link #MONGO_PORT} settings 
     */
    public MongoDatastore(MongoMappingContext mappingContext, Map<String, String> connectionDetails,
            MongoOptions mongoOptions) {

        this(mappingContext, connectionDetails);
        if (mongoOptions != null)
            this.mongoOptions = mongoOptions;
    }

    /**
     * Constructs a MongoDatastore using the given MappingContext and connection details map.
     * 
     * @param mappingContext The MongoMappingContext
     * @param connectionDetails The connection details containing the {@link #MONGO_HOST} and {@link #MONGO_PORT} settings 
     */
    public MongoDatastore(MongoMappingContext mappingContext, Map<String, String> connectionDetails) {
        super(mappingContext, connectionDetails);

        if (mappingContext != null)
            mappingContext.addMappingContextListener(this);

        initializeConverters(mappingContext);
        mappingContext.getConverterRegistry().addConverter(new Converter<String, ObjectId>() {
            @Override
            public ObjectId convert(String source) {
                return new ObjectId(source);
            }
        });
        mappingContext.getConverterRegistry().addConverter(new Converter<ObjectId, String>() {
            @Override
            public String convert(ObjectId source) {
                return source.toString();
            }
        });
    }

    public MongoDatastore(MongoMappingContext mappingContext) {
        this(mappingContext, Collections.<String, String>emptyMap());
    }

    /**
     * Constructor for creating a MongoDatastore using an existing Mongo instance
     * @param mappingContext The MappingContext
     * @param mongo The existing Mongo instance
     */
    public MongoDatastore(MongoMappingContext mappingContext, Mongo mongo) {
        this(mappingContext, Collections.<String, String>emptyMap());
        this.mongo = mongo;
    }

    /**
     * Constructor for creating a MongoDatastore using an existing Mongo instance. In this case
     * the connection details are only used to supply a USERNAME and PASSWORD
     * 
     * @param mappingContext The MappingContext
     * @param mongo The existing Mongo instance
     */
    public MongoDatastore(MongoMappingContext mappingContext, Mongo mongo, Map<String, String> connectionDetails) {
        this(mappingContext, connectionDetails);
        this.mongo = mongo;
    }

    public Mongo getMongo() {
        return mongo;
    }

    public MongoTemplate getMongoTemplate(PersistentEntity entity) {
        return mongoTemplates.get(entity);
    }

    @Override
    protected Session createSession(Map<String, String> connectionDetails) {
        return new MongoSession(this, getMappingContext());
    }

    public void afterPropertiesSet() throws Exception {
        if (this.mongo == null) {
            ServerAddress defaults = new ServerAddress();
            MongoFactoryBean dbFactory = new MongoFactoryBean();
            dbFactory.setHost(read(String.class, MONGO_HOST, connectionDetails, defaults.getHost()));
            dbFactory.setPort(read(Integer.class, MONGO_PORT, connectionDetails, defaults.getPort()));
            if (mongoOptions != null) {
                dbFactory.setMongoOptions(mongoOptions);
            }
            dbFactory.afterPropertiesSet();

            this.mongo = dbFactory.getObject();
        }

        for (PersistentEntity entity : mappingContext.getPersistentEntities()) {
            createMongoTemplate(entity, mongo);
        }
    }

    protected void createMongoTemplate(PersistentEntity entity, Mongo mongoInstance) {
        DocumentMappingContext dc = (DocumentMappingContext) getMappingContext();
        String collectionName = entity.getDecapitalizedName();
        String databaseName = dc.getDefaultDatabaseName();
        ClassMapping<MongoCollection> mapping = entity.getMapping();
        final MongoCollection mongoCollection = mapping.getMappedForm() != null ? mapping.getMappedForm() : null;

        if (mongoCollection != null) {
            if (mongoCollection.getCollection() != null)
                collectionName = mongoCollection.getCollection();
            if (mongoCollection.getDatabase() != null)
                databaseName = mongoCollection.getDatabase();

        }
        final MongoTemplate mt = new MongoTemplate(mongoInstance, databaseName, collectionName);

        String username = read(String.class, USERNAME, connectionDetails, null);
        String password = read(String.class, PASSWORD, connectionDetails, null);

        if (username != null && password != null) {
            mt.setUsername(username);
            mt.setPassword(password);
        }

        if (mongoCollection != null) {
            final WriteConcern writeConcern = mongoCollection.getWriteConcern();
            if (writeConcern != null) {
                mt.executeInSession(new DbCallback<Object>() {
                    @Override
                    public Object doInDB(DB db) throws MongoException, DataAccessException {

                        if (writeConcern != null) {
                            DBCollection collection = db.getCollection(mt.getDefaultCollectionName());
                            collection.setWriteConcern(writeConcern);
                        }
                        return null;
                    }
                });
            }

        }

        try {
            mt.afterPropertiesSet();
        } catch (Exception e) {
            throw new DatastoreConfigurationException(
                    "Failed to configure Mongo template for entity [" + entity + "]: " + e.getMessage(), e);
        }

        mongoTemplates.put(entity, mt);

        initializeIndices(entity, mt);
    }

    /**
     * Indexes any properties that are mapped with index:true
     * @param entity The entity
     * @param template The template
     */
    protected void initializeIndices(final PersistentEntity entity, final MongoTemplate template) {
        template.execute(new DbCallback<Object>() {
            public Object doInDB(DB db) throws MongoException, DataAccessException {
                final DBCollection collection = db.getCollection(template.getDefaultCollectionName());

                final ClassMapping<MongoCollection> classMapping = entity.getMapping();
                if (classMapping != null) {
                    final MongoCollection mappedForm = classMapping.getMappedForm();
                    if (mappedForm != null) {
                        for (Map compoundIndex : mappedForm.getCompoundIndices()) {
                            DBObject indexDef = new BasicDBObject(compoundIndex);
                            collection.ensureIndex(indexDef);
                        }

                    }
                }

                for (PersistentProperty<MongoAttribute> property : entity.getPersistentProperties()) {
                    final boolean indexed = isIndexed(property);

                    if (indexed) {

                        final MongoAttribute mongoAttributeMapping = property.getMapping().getMappedForm();

                        DBObject dbObject = new BasicDBObject();
                        final String fieldName = getMongoFieldNameForProperty(property);
                        dbObject.put(fieldName, 1);
                        DBObject options = new BasicDBObject();
                        if (mongoAttributeMapping != null) {
                            final Map attributes = mongoAttributeMapping.getIndexAttributes();
                            if (attributes != null) {
                                if (attributes.containsKey(MongoAttribute.INDEX_TYPE)) {
                                    dbObject.put(fieldName, attributes.remove(MongoAttribute.INDEX_TYPE));
                                }
                                options.putAll(attributes);
                            }
                        }
                        if (options.toMap().isEmpty()) {
                            collection.ensureIndex(dbObject);
                        } else {
                            collection.ensureIndex(dbObject, options);
                        }
                    }
                }

                return null;
            }

            String getMongoFieldNameForProperty(PersistentProperty<MongoAttribute> property) {
                PropertyMapping<MongoAttribute> pm = property.getMapping();
                String propKey = null;
                if (pm.getMappedForm() != null) {
                    propKey = pm.getMappedForm().getField();
                }
                if (propKey == null) {
                    propKey = property.getName();
                }
                return propKey;
            }
        });
    }

    public void persistentEntityAdded(PersistentEntity entity) {
        createMongoTemplate(entity, this.mongo);
    }

}