couchdb.CouchdbClient.java Source code

Java tutorial

Introduction

Here is the source code for couchdb.CouchdbClient.java

Source

package couchdb;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import org.ektorp.CouchDbConnector;
import org.ektorp.DocumentNotFoundException;
import org.ektorp.UpdateConflictException;
import org.ektorp.ViewQuery;
import org.ektorp.ViewResult;
import org.ektorp.ViewResult.Row;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import com.yahoo.ycsb.ByteIterator;
import com.yahoo.ycsb.DB;
import com.yahoo.ycsb.DBException;
import com.yahoo.ycsb.StringByteIterator;

import couchdb.StringToStringMap;

/*
 * Copyright 2013 KU Leuven Research and Development - iMinds - Distrinet
 * 
 * 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.
 * 
 * Administrative Contact: dnet-project-office@cs.kuleuven.be
 * Technical Contact: arnaud.schoonjans@student.kuleuven.be
 */
public class CouchdbClient extends DB {

    // Default configuration
    private static final String DEFAULT_DATABASE_NAME = "usertable";
    private static final int DEFAULT_COUCHDB_PORT_NUMBER = 5984;
    private static final String PROTOCOL = "http";
    // Database connector
    private CouchDbConnector dbConnector;
    // Return codes
    private static final int OK = 0;
    private static final int UPDATE_CONFLICT = -2;
    private static final int DOC_NOT_FOUND = -3;
    private static final int JSON_PARSING_FAULT = -4;

    public CouchdbClient() {
        this.dbConnector = null;
    }

    // Constructor for testing purposes
    public CouchdbClient(List<URL> urls) {
        if (urls == null)
            throw new IllegalArgumentException("urls is null");
        this.dbConnector = new LoadBalancedConnector(urls, DEFAULT_DATABASE_NAME);
    }

    private List<URL> getUrlsForHosts() throws DBException {
        List<URL> result = new ArrayList<URL>();
        String hosts = getProperties().getProperty("hosts");
        String[] differentHosts = hosts.split(",");
        for (String host : differentHosts) {
            URL url = this.getUrlForHost(host);
            result.add(url);
        }
        return result;
    }

    private URL getUrlForHost(String host) throws DBException {
        String[] hostAndPort = host.split(":");
        try {
            if (hostAndPort.length == 1) {
                return new URL(PROTOCOL, host, DEFAULT_COUCHDB_PORT_NUMBER, "");
            } else {
                int portNumber = Integer.parseInt(hostAndPort[1]);
                return new URL(PROTOCOL, hostAndPort[0], portNumber, "");
            }
        } catch (MalformedURLException exc) {
            throw new DBException("Invalid host specified");
        } catch (NumberFormatException exc) {
            throw new DBException("Invalid port number specified");
        }
    }

    @Override
    public void init() throws DBException {
        List<URL> urls = getUrlsForHosts();
        this.dbConnector = new LoadBalancedConnector(urls, DEFAULT_DATABASE_NAME);
    }

    @Override
    public void cleanup() throws DBException {
        // Do nothing
    }

    private StringToStringMap executeReadOperation(String key) {
        try {
            return this.dbConnector.get(StringToStringMap.class, key);
        } catch (DocumentNotFoundException exc) {
            return null;
        }
    }

    private int executeWriteOperation(String key, StringToStringMap dataToWrite) {
        try {
            dataToWrite.put("_id", key);
            this.dbConnector.create(dataToWrite);
        } catch (UpdateConflictException exc) {
            return UPDATE_CONFLICT;
        }
        return OK;
    }

    private int executeDeleteOperation(StringToStringMap dataToDelete) {
        try {
            this.dbConnector.delete(dataToDelete);
        } catch (UpdateConflictException exc) {
            return UPDATE_CONFLICT;
        }
        return OK;
    }

    private int executeUpdateOperation(StringToStringMap dataToUpdate) {
        try {
            this.dbConnector.update(dataToUpdate);
        } catch (UpdateConflictException exc) {
            return UPDATE_CONFLICT;
        }
        return OK;
    }

    private void copyRequestedFieldsToResultMap(Set<String> fields, StringToStringMap inputMap,
            HashMap<String, ByteIterator> result) {
        for (String field : fields) {
            ByteIterator value = inputMap.getAsByteIt(field);
            result.put(field, value);
        }
        ByteIterator _id = inputMap.getAsByteIt("_id");
        ByteIterator _rev = inputMap.getAsByteIt("_rev");
        result.put("_id", _id);
        result.put("_rev", _rev);
    }

    private void copyAllFieldsToResultMap(StringToStringMap inputMap, HashMap<String, ByteIterator> result) {
        for (String field : inputMap.keySet()) {
            ByteIterator value = inputMap.getAsByteIt(field);
            result.put(field, value);
        }
    }

    // Table variable is not used => already contained in database connector
    @Override
    public int read(String table, String key, Set<String> fields, HashMap<String, ByteIterator> result) {
        StringToStringMap queryResult = this.executeReadOperation(key);
        if (queryResult == null)
            return DOC_NOT_FOUND;
        if (fields == null) {
            this.copyAllFieldsToResultMap(queryResult, result);
        } else {
            this.copyRequestedFieldsToResultMap(fields, queryResult, result);
        }
        return OK;
    }

    @Override
    public int scan(String table, String startkey, int recordcount, Set<String> fields,
            Vector<HashMap<String, ByteIterator>> result) {
        ViewResult viewResult = this.executeView(startkey, recordcount);
        for (Row row : viewResult.getRows()) {
            JSONObject jsonObj = this.parseAsJsonObject(row.getDoc());
            if (jsonObj == null)
                return JSON_PARSING_FAULT;
            if (fields == null) {
                @SuppressWarnings("unchecked")
                Set<String> requestedFields = jsonObj.keySet();
                result.add(this.getFieldsFromJsonObj(requestedFields, jsonObj));
            } else {
                result.add(this.getFieldsFromJsonObj(fields, jsonObj));
            }
        }
        return OK;
    }

    private ViewResult executeView(String startKey, int amountOfRecords) {
        ViewQuery query = new ViewQuery().viewName("_all_docs").startKey(startKey).limit(amountOfRecords)
                .includeDocs(true);
        return this.dbConnector.queryView(query);
    }

    private JSONObject parseAsJsonObject(String stringToParse) {
        JSONParser parser = new JSONParser();
        try {
            return (JSONObject) parser.parse(stringToParse);
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }

    private HashMap<String, ByteIterator> getFieldsFromJsonObj(Set<String> fields, JSONObject jsonObj) {
        HashMap<String, ByteIterator> result = new HashMap<String, ByteIterator>();
        for (String key : fields) {
            String value = jsonObj.get(key).toString();
            result.put(key, new StringByteIterator(value));
        }
        return result;
    }

    // Table variable is not used => already contained in database connector
    @Override
    public int update(String table, String key, HashMap<String, ByteIterator> values) {
        StringToStringMap queryResult = this.executeReadOperation(key);
        if (queryResult == null)
            return DOC_NOT_FOUND;
        StringToStringMap updatedMap = this.updateFields(queryResult, values);
        return this.executeUpdateOperation(updatedMap);
    }

    private StringToStringMap updateFields(StringToStringMap toUpdate, HashMap<String, ByteIterator> newValues) {
        for (String updateField : newValues.keySet()) {
            ByteIterator newValue = newValues.get(updateField);
            toUpdate.put(updateField, newValue);
        }
        return toUpdate;
    }

    // Table variable is not used => already contained in database connector
    @Override
    public int insert(String table, String key, HashMap<String, ByteIterator> values) {
        StringToStringMap dataToInsert = new StringToStringMap(values);
        return this.executeWriteOperation(key, dataToInsert);
    }

    // Table variable is not used => already contained in database connector
    @Override
    public int delete(String table, String key) {
        StringToStringMap toDelete = this.executeReadOperation(key);
        if (toDelete == null)
            return DOC_NOT_FOUND;
        return this.executeDeleteOperation(toDelete);
    }

}