Java tutorial
/******************************************************************************* * 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<String> operators = new HashSet<String>(); { 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<String> 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 + "]"; } }