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

org.usergrid.mongo.protocol.OpQuery Maven / Gradle / Ivy

There is a newer version: 0.0.27.1
Show newest version
/*******************************************************************************
 * Copyright 2012 Apigee Corporation
 * 
 * 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.usergrid.mongo.protocol;

import static org.apache.commons.collections.MapUtils.getIntValue;
import static org.usergrid.utils.JsonUtils.toJsonMap;
import static org.usergrid.utils.MapUtils.entry;
import static org.usergrid.utils.MapUtils.map;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Stack;

import org.antlr.runtime.ClassicToken;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.bson.BSONObject;
import org.bson.BasicBSONObject;
import org.bson.types.BasicBSONList;
import org.bson.types.ObjectId;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferInputStream;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.usergrid.management.ApplicationInfo;
import org.usergrid.management.UserInfo;
import org.usergrid.mongo.MongoChannelHandler;
import org.usergrid.mongo.commands.MongoCommand;
import org.usergrid.mongo.query.MongoQueryParser;
import org.usergrid.mongo.utils.BSONUtils;
import org.usergrid.persistence.Entity;
import org.usergrid.persistence.EntityManager;
import org.usergrid.persistence.Identifier;
import org.usergrid.persistence.Query;
import org.usergrid.persistence.Results;
import org.usergrid.persistence.Schema;
import org.usergrid.persistence.Query.SortDirection;
import org.usergrid.persistence.query.tree.AndOperand;
import org.usergrid.persistence.query.tree.Equal;
import org.usergrid.persistence.query.tree.GreaterThan;
import org.usergrid.persistence.query.tree.GreaterThanEqual;
import org.usergrid.persistence.query.tree.LessThan;
import org.usergrid.persistence.query.tree.LessThanEqual;
import org.usergrid.persistence.query.tree.Operand;
import org.usergrid.persistence.query.tree.OrOperand;
import org.usergrid.security.shiro.PrincipalCredentialsToken;
import org.usergrid.security.shiro.utils.SubjectUtils;
import org.usergrid.utils.MapUtils;

public class OpQuery extends OpCrud {

    private static final Logger logger = LoggerFactory.getLogger(OpQuery.class);

    int flags;
    int numberToSkip;
    int numberToReturn;
    BSONObject query;
    BSONObject returnFieldSelector;

    static Set operators = new HashSet();
    {
        operators.add("all");
        operators.add("and");
        operators.add("elemMatch");
        operators.add("exists");
        operators.add("gt");
        operators.add("gte");
        operators.add("in");
        operators.add("lt");
        operators.add("lte");
        operators.add("mod");
        operators.add("ne");
        operators.add("nin");
        operators.add("nor");
        operators.add("not");
        operators.add("or");
        operators.add("regex");
        operators.add("size");
        operators.add("type");
        operators.add("where");
    }

    public OpQuery() {
        opCode = OP_QUERY;
    }

   
   

    public int getFlags() {
        return flags;
    }

    public void setFlags(int flags) {
        this.flags = flags;
    }

    public int getNumberToSkip() {
        return numberToSkip;
    }

    public void setNumberToSkip(int numberToSkip) {
        this.numberToSkip = numberToSkip;
    }

    public int getNumberToReturn() {
        return numberToReturn;
    }

    public void setNumberToReturn(int numberToReturn) {
        this.numberToReturn = numberToReturn;
    }

    public BSONObject getQuery() {
        return query;
    }

    public void setQuery(BSONObject query) {
        this.query = query;
    }

    public void setQuery(Map map) {
        query = new BasicBSONObject();
        query.putAll(map);
    }

    public BSONObject getReturnFieldSelector() {
        return returnFieldSelector;
    }

    public void setReturnFieldSelector(BSONObject returnFieldSelector) {
        this.returnFieldSelector = returnFieldSelector;
    }

    public void setReturnFieldSelector(Map map) {
        returnFieldSelector = new BasicBSONObject();
        returnFieldSelector.putAll(map);
    }

    @Override
    public void decode(ChannelBuffer buffer) throws IOException {
        super.decode(buffer);
        flags = buffer.readInt();
        fullCollectionName = readCString(buffer);
        numberToSkip = buffer.readInt();
        numberToReturn = buffer.readInt();
        query = BSONUtils.decoder().readObject(
                new ChannelBufferInputStream(buffer));
        if (buffer.readable()) {
            returnFieldSelector = BSONUtils.decoder().readObject(
                    new ChannelBufferInputStream(buffer));
          logger.info("found fieldSeclector: {}", returnFieldSelector);
        }
    }

    @Override
    public ChannelBuffer encode(ChannelBuffer buffer) {
        int l = 28; // 7 ints * 4 bytes

        ByteBuffer fullCollectionNameBytes = getCString(fullCollectionName);
        l += fullCollectionNameBytes.capacity();

        ByteBuffer queryBytes = encodeDocument(query);
        l += queryBytes.capacity();

        ByteBuffer returnFieldSelectorBytes = encodeDocument(returnFieldSelector);
        l += returnFieldSelectorBytes.capacity();

        messageLength = l;

        buffer = super.encode(buffer);

        buffer.writeInt(flags);

        buffer.writeBytes(fullCollectionNameBytes);

        buffer.writeInt(numberToSkip);
        buffer.writeInt(numberToReturn);

        buffer.writeBytes(queryBytes);

        buffer.writeBytes(returnFieldSelectorBytes);

        return buffer;
    }


    /*
     * (non-Javadoc)
     * 
     * @see org.usergrid.mongo.protocol.OpCrud#doOp()
     */
    @Override
    public OpReply doOp(MongoChannelHandler handler, ChannelHandlerContext ctx,
            MessageEvent messageEvent) {
        logger.debug("In OpQuery.doOp with fullCollectionName: {}", fullCollectionName);
        Subject currentUser = SubjectUtils.getSubject();

        String collectionName = getCollectionName();
        
        if ("$cmd".equals(collectionName)) {
           
            @SuppressWarnings("unchecked")
            String commandName = (String) MapUtils.getFirstKey(getQuery().toMap());
            
            if ("authenticate".equals(commandName)) {
                return handleAuthenticate(handler, getDatabaseName());
            }
            
            if ("getnonce".equals(commandName)) {
                return handleGetnonce();
            }
            
            if (!currentUser.isAuthenticated()) {
                return handleUnauthorizedCommand(messageEvent);
            }

            MongoCommand command = MongoCommand.getCommand(commandName);

            if (command != null) {
                logger.info("found command {} from name {}", command.getClass().getName(), commandName);
                return command.execute(handler, ctx, messageEvent, this);
            } else {
                logger.info("No command for " + commandName);
            }
        }

        if (!currentUser.isAuthenticated()) {
            return handleUnauthorizedQuery(messageEvent);
        }
        
        if ("system.namespaces".equals(collectionName)) {
            return handleListCollections(handler, getDatabaseName());
        } 
        
        if ("system.users".equals(collectionName)) {
            return handleListUsers();
        }
        
        return handleQuery(handler);

    }

    private OpReply handleAuthenticate(MongoChannelHandler handler,
            String databaseName) {
        logger.info("Authenticating for database " + databaseName + "... ");
        String name = (String) query.get("user");
        String nonce = (String) query.get("nonce");
        String key = (String) query.get("key");

        UserInfo user = null;
        try {
            user = handler.getOrganizations().verifyMongoCredentials(name,
                    nonce, key);
        } catch (Exception e1) {
            return handleAuthFails(this);
        }
        if (user == null) {
            return handleAuthFails(this);
        }

        PrincipalCredentialsToken token = PrincipalCredentialsToken
                .getFromAdminUserInfoAndPassword(user, key);
        Subject subject = SubjectUtils.getSubject();

        try {
            subject.login(token);
        } catch (AuthenticationException e2) {
            return handleAuthFails(this);
        }

        OpReply reply = new OpReply(this);
        reply.addDocument(map("ok", 1.0));
        return reply;
    }

    private OpReply handleGetnonce() {
        String nonce = String.format("%04x", (new Random()).nextLong());
        OpReply reply = new OpReply(this);
        reply.addDocument(map(entry("nonce", nonce), entry("ok", 1.0)));
        return reply;
    }

    private OpReply handleUnauthorizedCommand(MessageEvent e) {
        // { "assertion" : "unauthorized db:admin lock type:-1 client:127.0.0.1"
        // , "assertionCode" : 10057 , "errmsg" : "db assertion failure" , "ok"
        // : 0.0}
        OpReply reply = new OpReply(this);
        reply.addDocument(map(
                entry("assertion",
                        "unauthorized db:"
                                + getDatabaseName()
                                + " lock type:-1 client:"
                                + ((InetSocketAddress) e.getRemoteAddress())
                                        .getAddress().getHostAddress()),
                entry("assertionCode", 10057),
                entry("errmsg", "db assertion failure"), entry("ok", 0.0)));
        return reply;
    }

    private OpReply handleUnauthorizedQuery(MessageEvent e) {
        // { "$err" : "unauthorized db:test lock type:-1 client:127.0.0.1" ,
        // "code" : 10057}
        OpReply reply = new OpReply(this);
        reply.addDocument(map(
                entry("$err",
                        "unauthorized db:"
                                + getDatabaseName()
                                + " lock type:-1 client:"
                                + ((InetSocketAddress) e.getRemoteAddress())
                                        .getAddress().getHostAddress()),
                entry("code", 10057)));
        return reply;
    }

    private OpReply handleAuthFails(OpQuery opQuery) {
        // { "errmsg" : "auth fails" , "ok" : 0.0}
        OpReply reply = new OpReply(opQuery);
        reply.addDocument(map(entry("errmsg", "auth fails"), entry("ok", 0.0)));
        return reply;
    }

    private OpReply handleListCollections(MongoChannelHandler handler,
            String databaseName) {
        logger.info("Handling list collections for database {} ... ",
                databaseName);
        Identifier id = Identifier.from(databaseName);

        OpReply reply = new OpReply(this);
        
        ApplicationInfo application = SubjectUtils.getApplication(id);

        if (application == null) {
            return reply;
        }
       
        EntityManager em = handler.getEmf().getEntityManager(application.getId());
        
        
        try {
            Set collections = em.getApplicationCollections();
            for (String colName : collections) {
                if (Schema.isAssociatedEntityType(colName)) {
                    continue;
                }
                reply.addDocument(map("name", String.format("%s.%s", databaseName, colName)));
                reply.addDocument(map("name", String.format("%s.%s.$_id_", databaseName , colName)));
            }
            // reply.addDocument(map("name", databaseName + ".system.indexes"));
        } catch (Exception ex) {
            logger.error("Unable to retrieve collections", ex);
        }
        return reply;
    }

    private OpReply handleListUsers() {
        logger.info("Handling list users for database {} ...  " ,  getDatabaseName());

        OpReply reply = new OpReply(this);
        return reply;
    }

    private OpReply handleQuery(MongoChannelHandler handler) {
        logger.info("Handling a query... ");
        OpReply reply = new OpReply(this);
        
        ApplicationInfo application = SubjectUtils.getApplication(Identifier
                .from(getDatabaseName()));
        if (application == null) {
            return reply;
        }
        
        int count = getNumberToReturn();
        if (count <= 0) {
            count = 30;
        }
        
        EntityManager em = handler.getEmf().getEntityManager(
                application.getId());
        
        try {
            Results results = null;
            Query q = MongoQueryParser.toNativeQuery(query, returnFieldSelector, numberToReturn);
            if (q != null) {
                results = em.searchCollection(em.getApplicationRef(),
                        getCollectionName(), q);

            } else {
                results = em.getCollection(em.getApplicationRef(),
                        getCollectionName(), null, count,
                        Results.Level.ALL_PROPERTIES, false);
            }
            if (!results.isEmpty()) {
                for (Entity entity : results.getEntities()) {
                    
                    Object savedId = entity.getProperty("_id");
                    Object mongoId = null;
                    
                    //try to parse it into an ObjectId
                    if(savedId == null){
                        mongoId = entity.getUuid();
                    }else{
                        try{
                            mongoId = new ObjectId(savedId.toString());
                        //it's not a mongo Id, use it as is
                        }catch(IllegalArgumentException iae){
                            mongoId = savedId;
                        }
                    }
                    
                    reply.addDocument(map(
                            entry("_id", mongoId),
                            toJsonMap(entity),
                            entry(Schema.PROPERTY_UUID, entity.getUuid()
                                    .toString())));
                }
            }
        } catch (Exception ex) {
            logger.error("Unable to retrieve collections", ex);
        }
        return reply;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "OpQuery [flags=" + flags + ", numberToSkip=" + numberToSkip
                + ", numberToReturn=" + numberToReturn + ", query=" + query
                + ", returnFieldSelector=" + returnFieldSelector
                + ", fullCollectionName=" + fullCollectionName
                + ", messageLength=" + messageLength + ", requestID="
                + requestID + ", responseTo=" + responseTo + ", opCode="
                + opCode + "]";
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy