/**
* Copyright (c) 2009-2011 VMware, Inc. All Rights Reserved.
*
* 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.springsource.insight.plugin.mongodb;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bson.types.ObjectId;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.CommandResult;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.ServerAddress;
import com.mongodb.WriteConcern;
import com.mongodb.WriteResult;
import com.springsource.insight.intercept.operation.Operation;
import com.springsource.insight.util.StringFormatterUtils;
import com.springsource.insight.util.StringUtil;
/**
* Utilities for converting method arguments for MongoDB-related operations
*/
public final class MongoArgumentUtils {
private MongoArgumentUtils() {
throw new UnsupportedOperationException("No instance");
}
/**
* The maximum length of a string we generate
*/
public static final int MAX_STRING_LENGTH = 256;
/**
* These classes can just be converted willy-nilly
*/
private static final Class<?>[] SIMPLE_CLASSES = new Class<?>[]{
String.class, Boolean.class, Byte.class, Character.class,
Short.class, Integer.class, Long.class, Float.class, Double.class,
BigInteger.class, BigDecimal.class};
/**
* With care, we can treat these MongoDB classes simply too
*/
private static final Class<?>[] SIMPLE_MONGO_CLASSES = new Class<?>[]{
ObjectId.class, CommandResult.class, BasicDBList.class,
BasicDBObject.class, DBCollection.class, WriteConcern.class,
WriteResult.class};
/**
* A little helper interface to convert an {@link Object} to a
* {@link String}
*
* @param <T> Type of object being string-ified
*/
private interface StringForm<T> {
/**
* @param object guaranteed non-null
* @return The {@link String} representation of the object
*/
String stringify(T object);
}
/**
* Take an object from one of the "safe" classes, convert to a
* {@link String} and trim the result, perhaps using ellipses if we truncate
* it
*/
public static final StringForm<Object> DefaultStringForm = new StringForm<Object>() {
public String stringify(final Object object) {
return object.toString();
}
};
/**
* For a {@link DBCursor}, we get the {@link DBCollection} name, the query
* and the keys wanted
*/
public static final StringForm<DBCursor> DBCursorStringForm = new StringForm<DBCursor>() {
public String stringify(final DBCursor cursor) {
return "DBCursor(" + MongoArgumentUtils.toString(cursor.getQuery()) + ", "
+ MongoArgumentUtils.toString(cursor.getKeysWanted()) + ")";
}
};
/**
* This type is common for inserts. In fact, even a single insert gets
* converted to a {@link DBObject}[]
*/
public static final StringForm<DBObject[]> DBObjectArrayStringForm = new StringForm<DBObject[]>() {
public String stringify(final DBObject[] array) {
return "DBObject" + MongoArgumentUtils.toString(array);
}
};
/**
* A map from a {@link Class} to a helper ({@link StringForm}) that returns
* a suitable {@link String} value
* <p/>
* You'll get used to this style of object creation if you stare at it long
* enough. It's handy because you don't need to mention the name of the
* variable (STRING_FORM_MAP) in any of the put() calls, which you'd have to
* if you did it long hand.
*/
@SuppressWarnings("synthetic-access")
public static final Map<Class<?>, StringForm<? extends Object>> STRING_FORM_MAP = new HashMap<Class<?>, StringForm<? extends Object>>() {
private static final long serialVersionUID = 1L;
{
// Wrapper classes
//
for (Class<?> cls : SIMPLE_CLASSES) {
put(cls, DefaultStringForm);
}
// MongoDB classes
//
for (Class<?> cls : SIMPLE_MONGO_CLASSES) {
put(cls, DefaultStringForm);
}
put(DBCursor.class, DBCursorStringForm);
put(DBObject[].class, DBObjectArrayStringForm);
}
};
public static List<String> toString(final Object[] array) {
return toString(array, MAX_STRING_LENGTH);
}
/**
* Convert an {@link Object}[] to a {@link String}. Don't convert more than
* MAX_ARGS arguments and don't make it more than roughly maxLength long.
* <p/>
* Append ellipses to any argument we truncate, or to the whole array if
* it's too long.
*
* @param array
* @param maxLength
* @return
*/
public static List<String> toString(final Object[] array, final int maxLength) {
return new ArrayList<String>() {
private static final long serialVersionUID = 1L;
{
int soFar = 0;
for (final Object arg : array) {
final String result = MongoArgumentUtils.toString(arg, maxLength - soFar);
soFar += result.length();
add(result);
if (soFar >= maxLength) {
break;
}
}
}
};
}
public static String toString(final Object object) {
return toString(object, MAX_STRING_LENGTH);
}
/**
* Primitives and "safe" types get a call to {@link #toString()} via the
* {@link StringForm} helper class; everything else is just the class name.
*
* @param object
* @return
*/
public static String toString(final Object object, final int maxLength) {
if (object == null) {
return StringFormatterUtils.NULL_VALUE_STRING;
}
Class<? extends Object> cls = object.getClass();
@SuppressWarnings("unchecked")
StringForm<Object> stringForm = (StringForm<Object>) STRING_FORM_MAP.get(cls);
if (stringForm != null) {
return StringUtil.chopTailAndEllipsify(stringForm.stringify(object), maxLength);
}
return cls.getSimpleName();
}
public static String toString(final DBObject dbObject) {
return dbObject == null ? null : chopTailAndEllipsify(dbObject.toString());
}
public static String chopTailAndEllipsify(final String string) {
return StringUtil.chopTailAndEllipsify(string, MAX_STRING_LENGTH);
}
public static Operation putDatabaseDetails(Operation op, DB db) {
op.put("dbName", db.getName());
Mongo mongo = db.getMongo();
ServerAddress address = mongo.getAddress();
op.put("host", address.getHost());
op.put("port", address.getPort());
return op;
}
}