Java tutorial
/******************************************************************************* * Copyright 2011, 2012 CNES - CENTRE NATIONAL d'ETUDES SPATIALES * * This file is part of SITools2. * * SITools2 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SITools2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with SITools2. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package fr.cnes.sitools.datasource.mongodb.dbexplorer; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Logger; import org.bson.types.ObjectId; import org.restlet.Context; import org.restlet.data.CharacterSet; import org.restlet.data.Form; import org.restlet.data.Reference; import org.restlet.data.Status; import org.restlet.representation.Representation; import org.restlet.representation.Variant; import org.restlet.resource.Get; import org.restlet.resource.ResourceException; 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.MongoException; import com.thoughtworks.xstream.XStream; import fr.cnes.sitools.common.SitoolsResource; import fr.cnes.sitools.common.model.Response; import fr.cnes.sitools.datasource.mongodb.business.MongoDBExplorerRepresentation; import fr.cnes.sitools.datasource.mongodb.business.SitoolsMongoDBDataSource; import fr.cnes.sitools.datasource.mongodb.model.Collection; import fr.cnes.sitools.datasource.mongodb.model.Database; import fr.cnes.sitools.datasource.mongodb.model.MongoDBAttributeValue; import fr.cnes.sitools.datasource.mongodb.model.MongoDBRecord; /** * DBExplorerResource using DataSource for pooled connections * * @author jp.boignard (AKKA Technologies) */ public class MongoDBExplorerResource extends SitoolsResource { /** If the resource is a database, this contains its content. */ private volatile boolean databaseTarget; /** If the resource is a list of collection */ private volatile boolean collectionsTarget; /** If the resource is a collection, this contains its details. */ private volatile boolean collectionTarget; /** If the resource is a recordSet with pagination, this contains its content. */ private volatile boolean recordSetTarget; /** If the resource is a collection, this contains its fields. */ private volatile boolean metadataTarget; /** If the resource is a record, this contains its content. */ private volatile boolean recordTarget; /** if resource is relative to a schema or if schema is given */ private String collectionName; /** if resource is relative to a record */ private String idDocument; /** Logger current */ private Logger logger = Context.getCurrentLogger(); /** Max number of rows */ private int maxrows = 300; /** Fetch size */ private int fetchSize = 0; /** The parent DBexplorer application handler */ private volatile MongoDBExplorerApplication dbexplorer = null; /** Form */ private Form pagination = null; /** Base reference */ private String baseRef; /** The start index */ private int start = 0; /** The limit index */ private int limit = 0; @Override public void sitoolsDescribe() { setName("MongoDBExplorerResource"); setDescription("Explore mongoDB datasource"); } /** * Returns if target is a database * * @return true if database */ public final boolean isDatabaseTarget() { return databaseTarget; } /** * Returns if target is a record set * * @return true if record set */ public final boolean isRecordSetTarget() { return recordSetTarget; } /** * Returns if target is a record * * @return true if record */ public final boolean isRecordTarget() { return recordTarget; } @Override public final void doInit() { super.doInit(); // parent : dbexplorer this.dbexplorer = (MongoDBExplorerApplication) getApplication(); // target : database, table, record Map<String, Object> attributes = this.getRequest().getAttributes(); this.collectionName = (attributes.get("collectionName") != null) ? Reference.decode((String) attributes.get("collectionName"), CharacterSet.UTF_8) : null; this.idDocument = (attributes.get("_id") != null) ? Reference.decode((String) attributes.get("_id"), CharacterSet.UTF_8) : null; this.databaseTarget = (this.collectionName == null) && (this.idDocument == null) && !this.getReference().getLastSegment().equals("collections"); this.collectionsTarget = (this.collectionName == null) && (this.idDocument == null) && !this.databaseTarget; this.recordSetTarget = (this.collectionName != null) && (this.getReference().getLastSegment().equals("records")); this.metadataTarget = (this.collectionName != null) && (this.getReference().getLastSegment().equals("metadata")); this.collectionTarget = (this.collectionName != null) && (this.idDocument == null) && !recordSetTarget && !this.metadataTarget; this.recordTarget = (this.collectionName != null) && (this.idDocument != null); // parameters : pagination, ... this.pagination = this.getQuery(); // TODO baseRef / publicBaseRef // pas de / la fin... if (this.getReference().getBaseRef().toString().endsWith("/")) { this.baseRef = this.getReference().getBaseRef().toString().substring(1, this.getReference().getBaseRef().toString().length()); } else { this.baseRef = this.getReference().getBaseRef().toString(); } } /** * Get the DataSource * * @return the DataSource associated */ public final SitoolsMongoDBDataSource getDataSource() { return this.dbexplorer.getDataSource(); } /** * Process constraint * * @param variant * Client preference * @return Representation to be used */ public Representation processConstraint(Variant variant) { Representation represent = null; SitoolsMongoDBDataSource datasource = getDataSource(); DB database = datasource.getDatabase(); // DATABASE TARGET if (this.databaseTarget) { CommandResult cmd = database.getStats(); List<String> messages = new ArrayList<String>(); traceObjects(cmd, messages); Response response = new Response(true, messages, String.class, "statusInfo"); represent = getRepresentation(response, variant); } // COLLECTIONS TARGET => GET THE LIST OF COLLECTIONS if (this.collectionsTarget) { Database mongoDatabase = getCollections(database); Response response = new Response(true, mongoDatabase, Database.class, "mongodbdatabase"); return getRepresentation(response, variant); } // COLLECTION TARGET => GET THE DESCRIPTION OF THE COLLECTION if (this.collectionTarget) { Collection collection = getACollection(database, this.collectionName); Response response = new Response(true, collection, Collection.class, "collection"); return getRepresentation(response, variant); } if (this.recordSetTarget) { DBCollection collectionMongo = database.getCollection(this.collectionName); this.start = getPaginationStartRecord(); this.limit = getPaginationExtend(); limit = (limit > maxrows) ? maxrows : limit; if (limit == 0) { limit = maxrows; } DBCursor cursor = collectionMongo.find().skip(start).limit(limit); // cursor.setOptions(Bytes.QUERYOPTION_EXHAUST); return new MongoDBExplorerRepresentation(getMediaType(variant), cursor, this); } // TODO a marche pas trop if (this.recordTarget) { DBCollection collectionMongo = database.getCollection(this.collectionName); DBObject query = new BasicDBObject(); if (ObjectId.isValid(this.idDocument)) { query.put("_id", new ObjectId(this.idDocument)); } else { query.put("_id", this.idDocument); } DBCursor cursor = collectionMongo.find(query).limit(1); this.setStart(0); this.setLimit(1); return new MongoDBExplorerRepresentation(getMediaType(variant), cursor, this); } if (this.metadataTarget) { DBCollection collectionMongo = database.getCollection(this.collectionName); BasicDBObject object = (BasicDBObject) collectionMongo.findOne(); List<MongoDBAttributeValue> values = null; if (object != null && !object.isEmpty()) { values = getAttributeValue(object, false); } Response response = new Response(true, values, MongoDBAttributeValue.class, "fields"); return getRepresentation(response, variant); } return represent; } /** * Get a {@link Collection} from the MongoDB database and its name * * @param database * the database * @param colName * the name of the collection * @return a {@link Collection} from the MongoDB database and its name */ private Collection getACollection(DB database, String colName) { List<String> statusDetails = new ArrayList<String>(); DBCollection collectionMongo = database.getCollection(colName); Collection collection = new Collection(); collection.setName(colName); collection.setUrl(getBaseRef()); traceObjects(collectionMongo.getStats(), statusDetails); collection.setStatusDetails(statusDetails); return collection; } /** * Get a {@link Database} with the list of collections * * @param database * the MongoDB database * @return a {@link Database} with the list of collections * @throws ResourceException * if there is an error */ private Database getCollections(DB database) throws ResourceException { try { Set<String> collections = database.getCollectionNames(); Database mongoDatabase = new Database(); mongoDatabase.setUrl(getBaseRef()); for (String colName : collections) { Collection collection = new Collection(); collection.setName(colName); collection.setUrl(getBaseRef() + "/" + colName); mongoDatabase.getCollections().add(collection); } return mongoDatabase; } catch (MongoException e) { throw new ResourceException(Status.SERVER_ERROR_INTERNAL, e); } } /** * Gets if the given Object is a MongoDB object or not * * @param value * the object * @return true if object is a MongoDB object, false otherwise */ private boolean isObject(Object value) { return value.getClass().isAssignableFrom(BasicDBObject.class); } /** * Create a List of {@link MongoDBAttributeValue} from a {@link BasicDBObject} Each {@link MongoDBAttributeValue} * contains the key and its children. If withValue is true, it also contains the value * * @param dbObject * the {@link BasicDBObject} * @param withValue * true to set the value as well * @return the List of {@link MongoDBAttributeValue} */ private List<MongoDBAttributeValue> getAttributeValue(BasicDBObject dbObject, boolean withValue) { List<MongoDBAttributeValue> children = new ArrayList<MongoDBAttributeValue>(); for (String key : dbObject.keySet()) { Object value = dbObject.get(key); MongoDBAttributeValue attr = new MongoDBAttributeValue(); attr.setName(key.toString()); // TODO check null values attr.setType(dbObject.get(key).getClass().getSimpleName()); if (isObject(value)) { BasicDBObject dbObjectValue = (BasicDBObject) value; attr.setChildren(getAttributeValue(dbObjectValue, withValue)); } else if (withValue) { attr.setValue(dbObject.get(key)); } children.add(attr); } return children; } /** * Get an representation * * @param variant * the variant needed by the client * @return representation with the following variant */ @Get public Representation get(Variant variant) { return processConstraint(variant); } /** * Get base reference * * @return base reference */ public final String getBaseRef() { return this.baseRef; } /** * Read startRecord request parameter -> integer - 0 by default * * @return startRecord */ public final int getPaginationStartRecord() { String str = this.pagination.getFirstValue("start", true); try { int startrecord = ((str != null) && !str.equals("")) ? Integer.parseInt(str) : 0; return (startrecord > 0) ? startrecord : 0; } catch (NumberFormatException e) { logger.severe(e.getMessage()); return 0; } } /** * Read extend request parameter -> integer - maxrows by default * * @return extend */ public final int getPaginationExtend() { String nbHits = this.pagination.getFirstValue("limit", true); try { int extend; if (nbHits != null && !nbHits.equals("")) { extend = Integer.parseInt(nbHits); } else { extend = maxrows; } return (extend > 0) ? extend : 0; } catch (NumberFormatException e) { logger.severe(e.getMessage()); return maxrows; } } /** * Gets the fetchSize value * * @return the fetchSize */ public final int getFetchSize() { return fetchSize; } /** * Gets the maxrows value * * @return the maxrows */ public final int getMaxrows() { return maxrows; } /** * Configure the XStream * * @param xstream * the XStream to treat * @param response * the response used */ public void configure(XStream xstream, Response response) { xstream.autodetectAnnotations(false); xstream.alias("response", Response.class); // Because annotations are apparently missed xstream.omitField(Response.class, "itemName"); xstream.omitField(Response.class, "itemClass"); xstream.alias("collection", Collection.class); xstream.alias("attribute", MongoDBAttributeValue.class); xstream.alias("record", MongoDBRecord.class); // If a class is present inside the response, link the item alias with this class if (response.getItemClass() != null) { xstream.alias("item", Object.class, response.getItemClass()); } // If the object has a name, associate its name instead of item in the response if (response.getItemName() != null) { xstream.aliasField(response.getItemName(), Response.class, "item"); } } /** * Trace informations for a sitoolsDataSource * * @param object * The DBObject to trace * @param messages * ArrayList<String> messages */ private void traceObjects(DBObject object, List<String> messages) { for (Object key : object.keySet()) { messages.add(key + ": " + object.get(key.toString())); } } /** * Gets the start value * * @return the start */ public int getStart() { return start; } /** * Sets the value of start * * @param start * the start to set */ public void setStart(int start) { this.start = start; } /** * Gets the limit value * * @return the limit */ public int getLimit() { return limit; } /** * Sets the value of limit * * @param limit * the limit to set */ public void setLimit(int limit) { this.limit = limit; } }