com.cloudant.client.api.DatabaseImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.cloudant.client.api.DatabaseImpl.java

Source

/*
 * Copyright (c) 2016 IBM Corp. 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.cloudant.client.api;

import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.assertNotEmpty;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.close;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.createPost;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.getAsString;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.getResponse;
import static com.cloudant.client.org.lightcouch.internal.CouchDbUtil.getResponseList;

import com.cloudant.client.api.model.DbInfo;
import com.cloudant.client.api.model.FindByIndexOptions;
import com.cloudant.client.api.model.Index;
import com.cloudant.client.api.model.IndexField;
import com.cloudant.client.api.model.Params;
import com.cloudant.client.api.model.Permissions;
import com.cloudant.client.api.model.Shard;
import com.cloudant.client.api.views.AllDocsRequestBuilder;
import com.cloudant.client.api.views.ViewRequestBuilder;
import com.cloudant.client.internal.DatabaseURIHelper;
import com.cloudant.client.internal.URIBase;
import com.cloudant.client.internal.views.AllDocsRequestBuilderImpl;
import com.cloudant.client.internal.views.AllDocsRequestResponse;
import com.cloudant.client.internal.views.ViewQueryParameters;
import com.cloudant.client.org.lightcouch.Changes;
import com.cloudant.client.org.lightcouch.CouchDatabase;
import com.cloudant.client.org.lightcouch.CouchDbException;
import com.cloudant.client.org.lightcouch.NoDocumentException;
import com.cloudant.client.org.lightcouch.Response;
import com.cloudant.http.Http;
import com.cloudant.http.HttpConnection;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.reflect.TypeToken;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

/**
 * Contains a Database Public API implementation.
 *
 * Methods may throw a {@link NoDocumentException} if the database does not exist.
 *
 * @author Mario Briggs
 * @since 0.0.1
 */
class DatabaseImpl implements Database {

    static final Logger log = Logger.getLogger(DatabaseImpl.class.getCanonicalName());
    private CouchDatabase db;
    private CloudantClient client;
    private final URI apiV2DBSecurityURI;

    /**
     * @param client
     * @param db
     */
    DatabaseImpl(CloudantClient client, CouchDatabase db) {
        super();
        this.client = client;
        this.db = db;
        apiV2DBSecurityURI = new URIBase(client.getBaseUri()).path("_api").path("v2").path("db")
                .path(db.getDbName()).path("_security").build();
    }

    @Override
    public void setPermissions(String userNameorApikey, EnumSet<Permissions> permissions) {
        assertNotEmpty(userNameorApikey, "userNameorApikey");
        assertNotEmpty(permissions, "permissions");
        final JsonArray jsonPermissions = new JsonArray();
        for (Permissions s : permissions) {
            final JsonPrimitive permission = new JsonPrimitive(s.toString());
            jsonPermissions.add(permission);
        }

        // get existing permissions
        JsonObject perms = getPermissionsObject();

        // now set back
        JsonElement elem = perms.get("cloudant");
        if (elem == null) {
            perms.addProperty("_id", "_security");
            elem = new JsonObject();
            perms.add("cloudant", elem);
        }
        elem.getAsJsonObject().add(userNameorApikey, jsonPermissions);

        InputStream response = null;
        HttpConnection put = Http.PUT(apiV2DBSecurityURI, "application/json");
        put.setRequestBody(client.getGson().toJson(perms));
        try {
            response = client.couchDbClient.executeToInputStream(put);
            String ok = getAsString(response, "ok");
            if (!ok.equalsIgnoreCase("true")) {
                //raise exception
            }
        } finally {
            close(response);
        }
    }

    /**
     * @return /api/v2/db/$dbname/_security JSON data
     * @throws UnsupportedOperationException if called on a database that does not provide the
     *                                       Cloudant authorization API
     */
    private JsonObject getPermissionsObject() {
        try {
            return client.couchDbClient.get(apiV2DBSecurityURI, JsonObject.class);
        } catch (CouchDbException exception) {
            //currently we can't inspect the HttpResponse code
            //being in this catch block means it was not a 20x code
            //look for the "bad request" that implies the endpoint is not supported
            if (exception.getMessage().toLowerCase().contains("bad request")) {
                throw new UnsupportedOperationException("The methods getPermissions and "
                        + "setPermissions are not supported for this database, consider using the "
                        + "/db/_security endpoint.");
            } else {
                throw exception;
            }
        }
    }

    @Override
    public Map<String, EnumSet<Permissions>> getPermissions() {
        JsonObject perms = getPermissionsObject();
        return client.getGson().getAdapter(new TypeToken<Map<String, EnumSet<Permissions>>>() {
        }).fromJsonTree(perms);
    }

    @Override
    public List<Shard> getShards() {
        InputStream response = null;
        try {
            response = client.couchDbClient.get(new DatabaseURIHelper(db.getDBUri()).path("_shards").build());
            return getResponseList(response, client.getGson(), new TypeToken<List<Shard>>() {
            }.getType());
        } finally {
            close(response);
        }
    }

    @Override
    public Shard getShard(String docId) {
        assertNotEmpty(docId, "docId");
        return client.couchDbClient.get(new DatabaseURIHelper(db.getDBUri()).path("_shards").path(docId).build(),
                Shard.class);
    }

    @Override
    public void createIndex(String indexName, String designDocName, String indexType, IndexField[] fields) {
        JsonObject indexDefn = getIndexDefinition(indexName, designDocName, indexType, fields);
        createIndex(indexDefn.toString());
    }

    @Override
    public void createIndex(String indexDefinition) {
        assertNotEmpty(indexDefinition, "indexDefinition");
        InputStream putresp = null;
        URI uri = new DatabaseURIHelper(db.getDBUri()).path("_index").build();
        try {
            putresp = client.couchDbClient
                    .executeToInputStream(createPost(uri, indexDefinition, "application/json"));
            String result = getAsString(putresp, "result");
            if (result.equalsIgnoreCase("created")) {
                log.info(String.format("Created Index: '%s'", indexDefinition));
            } else {
                log.warning(String.format("Index already exists : '%s'", indexDefinition));
            }
        } finally {
            close(putresp);
        }
    }

    @Override
    public <T> List<T> findByIndex(String selectorJson, Class<T> classOfT) {
        return findByIndex(selectorJson, classOfT, new FindByIndexOptions());
    }

    @Override
    public <T> List<T> findByIndex(String selectorJson, Class<T> classOfT, FindByIndexOptions options) {
        assertNotEmpty(selectorJson, "selectorJson");
        assertNotEmpty(options, "options");

        URI uri = new DatabaseURIHelper(db.getDBUri()).path("_find").build();
        JsonObject body = getFindByIndexBody(selectorJson, options);
        InputStream stream = null;
        try {
            stream = client.couchDbClient
                    .executeToInputStream(createPost(uri, body.toString(), "application/json"));
            Reader reader = new InputStreamReader(stream, "UTF-8");
            JsonArray jsonArray = new JsonParser().parse(reader).getAsJsonObject().getAsJsonArray("docs");
            List<T> list = new ArrayList<T>();
            for (JsonElement jsonElem : jsonArray) {
                JsonElement elem = jsonElem.getAsJsonObject();
                T t = client.getGson().fromJson(elem, classOfT);
                list.add(t);
            }
            return list;
        } catch (UnsupportedEncodingException e) {
            // This should never happen as every implementation of the java platform is required
            // to support UTF-8.
            throw new RuntimeException(e);
        } finally {
            close(stream);
        }
    }

    @Override
    public List<Index> listIndices() {
        InputStream response = null;
        try {
            URI uri = new DatabaseURIHelper(db.getDBUri()).path("_index").build();
            response = client.couchDbClient.get(uri);
            return getResponseList(response, client.getGson(), new TypeToken<List<Index>>() {
            }.getType());
        } finally {
            close(response);
        }
    }

    @Override
    public void deleteIndex(String indexName, String designDocId) {
        assertNotEmpty(indexName, "indexName");
        assertNotEmpty(designDocId, "designDocId");
        URI uri = new DatabaseURIHelper(db.getDBUri()).path("_index").path(designDocId).path("json").path(indexName)
                .build();
        InputStream response = null;
        try {
            HttpConnection connection = Http.DELETE(uri);
            response = client.couchDbClient.executeToInputStream(connection);
            getResponse(response, Response.class, client.getGson());
        } finally {
            close(response);
        }
    }

    @Override
    public Search search(String searchIndexId) {
        return new Search(client, this, searchIndexId);
    }

    @Override
    public DesignDocumentManager getDesignDocumentManager() {
        return new DesignDocumentManager(this);
    }

    @Override
    public ViewRequestBuilder getViewRequestBuilder(String designDoc, String viewName) {
        return new ViewRequestBuilder(client, this, designDoc, viewName);
    }

    @Override
    public AllDocsRequestBuilder getAllDocsRequestBuilder() {
        return new AllDocsRequestBuilderImpl(new ViewQueryParameters<String, AllDocsRequestResponse.Revision>(
                client, this, "", "", String.class, AllDocsRequestResponse.Revision.class) {
            protected DatabaseURIHelper getViewURIBuilder() {
                return new DatabaseURIHelper(db.getDBUri()).path("_all_docs");
            }
        });
    }

    @Override
    public com.cloudant.client.api.Changes changes() {
        Changes couchDbChanges = db.changes();
        com.cloudant.client.api.Changes changes = new com.cloudant.client.api.Changes(couchDbChanges);
        return changes;
    }

    @Override
    public <T> T find(Class<T> classType, String id) {
        return db.find(classType, id);
    }

    @Override
    public <T> T find(Class<T> classType, String id, Params params) {
        assertNotEmpty(params, "params");
        return db.find(classType, id, params.getInternalParams());
    }

    @Override
    public <T> T find(Class<T> classType, String id, String rev) {
        return db.find(classType, id, rev);
    }

    @Override
    public <T> T findAny(Class<T> classType, String uri) {
        return db.findAny(classType, uri);
    }

    @Override
    public InputStream find(String id) {
        return db.find(id);
    }

    @Override
    public InputStream find(String id, String rev) {
        return db.find(id, rev);
    }

    @Override
    public boolean contains(String id) {
        return db.contains(id);
    }

    @Override
    public com.cloudant.client.api.model.Response save(Object object) {
        Response couchDbResponse = db.save(object);
        com.cloudant.client.api.model.Response response = new com.cloudant.client.api.model.Response(
                couchDbResponse);
        return response;
    }

    @Override
    public com.cloudant.client.api.model.Response save(Object object, int writeQuorum) {
        Response couchDbResponse = client.couchDbClient.put(getDBUri(), object, true, writeQuorum);
        com.cloudant.client.api.model.Response response = new com.cloudant.client.api.model.Response(
                couchDbResponse);
        return response;
    }

    @Override
    public com.cloudant.client.api.model.Response post(Object object) {
        Response couchDbResponse = db.post(object);
        com.cloudant.client.api.model.Response response = new com.cloudant.client.api.model.Response(
                couchDbResponse);
        return response;
    }

    @Override
    public com.cloudant.client.api.model.Response post(Object object, int writeQuorum) {
        assertNotEmpty(object, "object");
        InputStream response = null;
        try {
            URI uri = new DatabaseURIHelper(db.getDBUri()).query("w", writeQuorum).build();
            response = client.couchDbClient
                    .executeToInputStream(createPost(uri, client.getGson().toJson(object), "application/json"));
            Response couchDbResponse = getResponse(response, Response.class, client.getGson());
            com.cloudant.client.api.model.Response cloudantResponse = new com.cloudant.client.api.model.Response(
                    couchDbResponse);
            return cloudantResponse;
        } finally {
            close(response);
        }
    }

    @Override
    public com.cloudant.client.api.model.Response update(Object object) {
        Response couchDbResponse = db.update(object);
        com.cloudant.client.api.model.Response response = new com.cloudant.client.api.model.Response(
                couchDbResponse);
        return response;
    }

    @Override
    public com.cloudant.client.api.model.Response update(Object object, int writeQuorum) {
        Response couchDbResponse = client.couchDbClient.put(getDBUri(), object, false, writeQuorum);
        com.cloudant.client.api.model.Response response = new com.cloudant.client.api.model.Response(
                couchDbResponse);
        return response;
    }

    @Override
    public com.cloudant.client.api.model.Response remove(Object object) {
        Response couchDbResponse = db.remove(object);
        com.cloudant.client.api.model.Response response = new com.cloudant.client.api.model.Response(
                couchDbResponse);
        return response;
    }

    @Override
    public com.cloudant.client.api.model.Response remove(String id, String rev) {
        Response couchDbResponse = db.remove(id, rev);
        com.cloudant.client.api.model.Response response = new com.cloudant.client.api.model.Response(
                couchDbResponse);
        return response;
    }

    @Override
    public List<com.cloudant.client.api.model.Response> bulk(List<?> objects) {
        List<Response> couchDbResponseList = db.bulk(objects, false);
        List<com.cloudant.client.api.model.Response> cloudantResponseList = new ArrayList<com.cloudant.client.api.model.Response>();
        for (Response couchDbResponse : couchDbResponseList) {
            com.cloudant.client.api.model.Response response = new com.cloudant.client.api.model.Response(
                    couchDbResponse);
            cloudantResponseList.add(response);
        }
        return cloudantResponseList;
    }

    @Override
    public com.cloudant.client.api.model.Response saveAttachment(InputStream in, String name, String contentType) {
        Response couchDbResponse = db.saveAttachment(in, name, contentType);
        com.cloudant.client.api.model.Response response = new com.cloudant.client.api.model.Response(
                couchDbResponse);
        return response;
    }

    @Override
    public com.cloudant.client.api.model.Response saveAttachment(InputStream in, String name, String contentType,
            String docId, String docRev) {
        Response couchDbResponse = db.saveAttachment(in, name, contentType, docId, docRev);
        com.cloudant.client.api.model.Response response = new com.cloudant.client.api.model.Response(
                couchDbResponse);
        return response;
    }

    @Override
    public String invokeUpdateHandler(String updateHandlerUri, String docId, Params params) {
        assertNotEmpty(params, "params");
        return db.invokeUpdateHandler(updateHandlerUri, docId, params.getInternalParams());
    }

    @Override
    public URI getDBUri() {
        return db.getDBUri();
    }

    @Override
    public DbInfo info() {
        return client.couchDbClient.get(new DatabaseURIHelper(db.getDBUri()).getDatabaseUri(), DbInfo.class);
    }

    @Override
    public void ensureFullCommit() {
        db.ensureFullCommit();
    }

    // private helper methods

    /**
     * Form a create index json from parameters
     */
    private JsonObject getIndexDefinition(String indexName, String designDocName, String indexType,
            IndexField[] fields) {
        assertNotEmpty(fields, "index fields");
        JsonObject indexObject = new JsonObject();
        if (!(indexName == null || indexName.isEmpty())) {
            indexObject.addProperty("name", indexName);
        }
        if (!(designDocName == null || designDocName.isEmpty())) {
            indexObject.addProperty("ddoc", designDocName);
        }
        if (!(indexType == null || indexType.isEmpty())) {

            indexObject.addProperty("type", indexType);
        }

        JsonArray fieldsArray = new JsonArray();
        for (int i = 0; i < fields.length; i++) {
            JsonObject fieldObject = new JsonObject();
            fieldObject.addProperty(fields[i].getName(), fields[i].getOrder().toString());
            fieldsArray.add(fieldObject);
        }
        JsonObject arrayOfFields = new JsonObject();
        arrayOfFields.add("fields", fieldsArray);
        indexObject.add("index", arrayOfFields);

        return indexObject;
    }

    private JsonObject getFindByIndexBody(String selectorJson, FindByIndexOptions options) {

        JsonArray fieldsArray = new JsonArray();
        if (options.getFields().size() > 0) {
            for (String field : options.getFields()) {
                JsonPrimitive jsonField = client.getGson().fromJson(field, JsonPrimitive.class);
                fieldsArray.add(jsonField);
            }
        }

        JsonArray sortArray = new JsonArray();
        if (options.getSort().size() > 0) {

            for (IndexField sort : options.getSort()) {
                JsonObject sortObject = new JsonObject();
                sortObject.addProperty(sort.getName(), sort.getOrder().toString());
                sortArray.add(sortObject);
            }
        }

        JsonObject indexObject = new JsonObject();

        //parse and find if valid json issue #28
        JsonObject selectorObject = null;
        boolean isObject = true;
        try {
            selectorObject = getGson().fromJson(selectorJson, JsonObject.class);
        } catch (JsonParseException e) {
            isObject = false;
        }

        if (!isObject) {
            if (selectorJson.startsWith("\"selector\"")) {
                selectorJson = selectorJson.substring(selectorJson.indexOf(":") + 1, selectorJson.length()).trim();
                selectorObject = getGson().fromJson(selectorJson, JsonObject.class);
            } else {
                throw new JsonParseException("selectorJson should be valid json or like " + "\"selector\": {...} ");
            }
        }

        if (selectorObject.has("selector")) {
            indexObject.add("selector", selectorObject.get("selector"));
        } else {
            indexObject.add("selector", selectorObject);
        }

        if (fieldsArray.size() > 0) {
            indexObject.add("fields", fieldsArray);
        }
        if (sortArray.size() > 0) {
            indexObject.add("sort", sortArray);
        }
        if (options.getLimit() != null) {
            indexObject.addProperty("limit", options.getLimit());
        }
        if (options.getSkip() != null) {
            indexObject.addProperty("skip", options.getSkip());
        }
        if (options.getReadQuorum() != null) {
            indexObject.addProperty("r", options.getReadQuorum());
        }
        if (options.getUseIndex() != null) {
            indexObject.add("use_index", getGson().fromJson(options.getUseIndex(), JsonArray.class));
        }

        return indexObject;
    }

    Gson getGson() {
        return client.getGson();
    }
}