things.mongo.MongoConnector.java Source code

Java tutorial

Introduction

Here is the source code for things.mongo.MongoConnector.java

Source

/*
 * Things
 *
 * Copyright (c) 2014, Markus Binsteiner. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.
 */

package things.mongo;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import rx.Observable;
import things.connectors.IdWrapper;
import things.exceptions.NoSuchThingException;
import things.exceptions.TypeRuntimeException;
import things.thing.AbstractThingReader;
import things.thing.Thing;
import things.thing.ThingReader;
import things.thing.ThingWriter;
import things.utils.MatcherUtils;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Optional;

import static com.codahale.metrics.MetricRegistry.name;

/**
 * Project: things-to-build
 * <p>
 * Written by: Markus Binsteiner
 * Date: 10/05/14
 * Time: 10:58 PM
 */
public class MongoConnector extends AbstractThingReader implements ThingReader, ThingWriter {

    private static final Logger myLogger = LoggerFactory.getLogger(MongoConnector.class);
    private Timer find_all_timer;
    private Timer find_children_matching_timer;
    private Timer find_matching_timer;
    private Timer find_exact_type_matching_key_timer;
    private Timer find_exact_type_and_key_timer;
    @Autowired
    private MetricRegistry metrics;
    private MongoTemplate mongoTemplate;

    @Autowired
    public MongoConnector(MongoTemplate mongoTemplate) throws Exception {
        this.mongoTemplate = mongoTemplate;
    }

    @Override
    public <V> Thing<V> addChild(Thing<?> parent, Thing<V> child) {
        child.getParents().add(parent.getId());
        return saveThing(child);
    }

    @Override
    public boolean deleteThing(String id, Optional<String> type, Optional<String> key) {

        Optional<Thing<?>> thing = findThingForIdQuery(id);

        if (!thing.isPresent()) {
            return false;
        } else {
            Query q = new Query();
            try {
                q.addCriteria(Criteria.where("_id").is(new ObjectId(id)));
            } catch (IllegalArgumentException iae) {
                throw new NoSuchThingException(id);
            }
            mongoTemplate.remove(thing.get());
            return true;
        }
    }

    private Object extractId(MongoPersistentProperty id, Object value) {

        try {
            Field idField = id.getField();
            String idValue = (String) idField.get(value);
            return idValue;
        } catch (IllegalAccessException e) {
            throw new TypeRuntimeException("Can't extract id from type " + typeRegistry.getType(value),
                    typeRegistry.getType(value), e);
        } catch (ClassCastException cce) {
            throw new TypeRuntimeException(
                    "Can't extract id for type " + typeRegistry.getType(value) + ": id is not of String type",
                    typeRegistry.getType(value));
        }
    }

    @Override
    public Observable<? extends Thing<?>> findAllThings() {
        final Timer.Context context = find_all_timer.time();
        try {
            List<Thing> things = mongoTemplate.findAll(Thing.class);
            return Observable.from(things).map(t -> (Thing<?>) t);
        } finally {
            context.stop();
        }
    }

    @Override
    public Observable<? extends Thing<?>> findThingForId(String id) {

        Optional<Thing<?>> thing = findThingForIdQuery(id);

        if (thing.isPresent()) {
            return Observable.just(thing.get());
        } else {
            throw new NoSuchThingException(id);
        }
    }

    private Optional<Thing<?>> findThingForIdQuery(String id) {
        if (id == null) {
            throw new IllegalArgumentException("Id can't be null");
        }

        Query q = new Query();
        try {
            q.addCriteria(Criteria.where("_id").is(new ObjectId(id)));
        } catch (IllegalArgumentException iae) {
            throw new NoSuchThingException(id);
        }

        Thing thing = mongoTemplate.findOne(q, Thing.class);

        if (thing == null) {
            return Optional.empty();
        }
        return Optional.of(thing);
    }

    @Override
    public Observable<? extends Thing<?>> findThingsMatchingTypeAndKey(final String type, final String key) {

        final Timer.Context context = find_matching_timer.time();
        try {
            Query q = new Query();
            String regexType = MatcherUtils.convertGlobToRegex(type);
            String regexKey = MatcherUtils.convertGlobToRegex(key);

            q.addCriteria(Criteria.where("thingType").regex(regexType).and("key").regex(regexKey));

            List<Thing> things = mongoTemplate.find(q, Thing.class);

            return Observable.from(things).map(t -> (Thing<?>) t);
        } finally {
            context.stop();
        }

    }

    @Override
    public Observable<? extends Thing<?>> getChildrenForId(String id) {

        Query q = new Query();

        q.addCriteria(Criteria.where("parents").is(id));

        List<Thing> result = mongoTemplate.find(q, Thing.class);
        return Observable.from(result).map(t -> (Thing<?>) t);
    }

    public Observable<? extends Thing<?>> getChildrenMatchingTypeAndKey(Thing<?> thing, String typeMatcher,
            String keyMatcher) {
        Query q = new Query();
        String regexType = MatcherUtils.convertGlobToRegex(typeMatcher);
        String regexKey = MatcherUtils.convertGlobToRegex(keyMatcher);
        q.addCriteria(Criteria.where("parents").is(thing.getId()).and("thingType").regex(regexType).and("key")
                .regex(regexKey));

        List<Thing> things = mongoTemplate.find(q, Thing.class);
        return Observable.from(things).map(t -> (Thing<?>) t);
    }

    @Override
    public Observable<? extends Thing<?>> getChildrenMatchingTypeAndKey(Observable<? extends Thing<?>> things,
            String typeMatcher, String keyMatcher) {

        return things.flatMap(t -> getChildrenMatchingTypeAndKey(t, typeMatcher, keyMatcher));
    }

    @Override
    public Observable<? extends Thing<?>> findThingsForTypeAndKey(String type, String key) {

        final Timer.Context context = find_exact_type_and_key_timer.time();
        try {
            Query q = new Query();

            q.addCriteria(Criteria.where("thingType").is(type).and("key").is(key));

            List<Thing> things = mongoTemplate.find(q, Thing.class);

            return Observable.from(things).map(t -> (Thing<?>) t);
        } finally {
            context.stop();
        }
    }

    @Override
    public Observable<? extends Thing<?>> findThingsForTypeMatchingKey(String type, String key) {

        final Timer.Context context = find_exact_type_matching_key_timer.time();
        try {
            Query q = new Query();
            String regexKey = MatcherUtils.convertGlobToRegex(key);

            q.addCriteria(Criteria.where("thingType").is(type).and("key").regex(regexKey));

            List<Thing> things = mongoTemplate.find(q, Thing.class);

            return Observable.from(things).map(t -> (Thing<?>) t);
        } finally {
            context.stop();
        }

    }

    private MongoPersistentProperty getIdField(Class valueClass) {
        MongoPersistentEntity<?> mongoPersistentEntity = mongoTemplate.getConverter().getMappingContext()
                .getPersistentEntity(valueClass);
        MongoPersistentProperty id = mongoPersistentEntity.getIdProperty();
        return id;
    }

    public MongoTemplate getMongoTemplate() {
        return mongoTemplate;
    }

    private boolean hasUsableId(Class valueClass) {
        if (getIdField(valueClass) == null) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public <V> V readValue(Thing<V> t) {

        return t.getValue();

        //        Query q = new Query();
        //        q.addCriteria(Criteria.where("_id").is(new ObjectId((String) t.getValue())));
        ////
        //        Class typeClass = typeRegistry.getTypeClass(t.getThingType());
        //        if ( hasUsableId(typeClass) ) {
        //            Object v = mongoTemplate.findOne(q, typeClass);
        //            return (V) v;
        //        } else {
        //            Object v = mongoTemplate.findOne(q, IdWrapper.class, t.getThingType());
        //            return (V) ((IdWrapper) v).getValue();
        //
        //        }
    }

    @Override
    public <V> Thing<V> saveThing(Thing<V> t) {
        t.setValueIsPopulated(true);
        myLogger.debug("Saving thing: " + t.toString());
        // mongo gives it an id if necessary
        mongoTemplate.save(t);
        myLogger.debug("Saved: " + t.toString());
        return t;
    }

    public Object saveValue(Optional valueId, Object value) {
        myLogger.debug("Saving value: " + value);

        Object vId = null;

        MongoPersistentProperty idField = getIdField(value.getClass());
        if (idField == null) {
            IdWrapper wrapper = new IdWrapper(value);
            mongoTemplate.save(wrapper, typeRegistry.getType(value));
            vId = wrapper.getId();
        } else {
            mongoTemplate.save(value, typeRegistry.getType(value));
            vId = extractId(idField, value);
        }

        return vId;
    }

    @Autowired
    public void setMetricRegistry(MetricRegistry reg) {
        this.metrics = reg;
        find_all_timer = metrics.timer(name(MongoConnector.class, "find-all"));
        find_matching_timer = metrics.timer(name(MongoConnector.class, "find-matching"));
        find_exact_type_and_key_timer = metrics.timer(name(MongoConnector.class, "find-exact-type-matching-key"));
        find_exact_type_matching_key_timer = metrics.timer(name(MongoConnector.class, "find-exact-type-and-key"));
        find_children_matching_timer = metrics.timer(name(MongoConnector.class, "find-matching"));
    }

}