cloudant / java-cloudant

A Java client for Cloudant
Apache License 2.0
79 stars 68 forks source link

Creating and executing a ViewMultipleRequest query that has no actual queries added to it -> NPE #383

Closed sevokn closed 7 years ago

sevokn commented 7 years ago

When using a ViewMultipleRequest, the individual queries are separated by a call to add().

If no actual add() call is performed (the set of queries to be executed in this multiple request is empty), the java-cloudant library crashes with a NullPointerException when executing the query, i.e. calling getViewResponses().

Exception in thread "main" java.lang.NullPointerException at com.cloudant.client.internal.views.ViewMultipleRequester.getViewResponses(ViewMultipleRequester.java:47)

Tested with version 2.9.0.

package bugtest;

import java.net.URL;
import java.util.List;

import com.cloudant.client.api.ClientBuilder;
import com.cloudant.client.api.CloudantClient;
import com.cloudant.client.api.Database;
import com.cloudant.client.api.views.Key;
import com.cloudant.client.api.views.MultipleRequestBuilder;
import com.cloudant.client.api.views.ViewMultipleRequest;
import com.cloudant.client.api.views.ViewRequestBuilder;
import com.cloudant.client.api.views.ViewResponse;
import com.google.gson.JsonNull;

public final class EmptyMultipleQueryBugTest {

    public static void main(final String[] args) throws Exception {
        final String urlString = "http://localhost:8080/";
        final URL url = new URL(urlString);
        final String username = "admin";
        final String password = "pass";
        final CloudantClient cloudantClient = ClientBuilder.url(url).username(username).password(password).build();
        final String databaseName = "emptymultiplequerybugtestdatabase";
        final boolean createDatabaseIfNotExist = true;
        final Database database = cloudantClient.database(databaseName, createDatabaseIfNotExist);
        final String designDocumentName = "designdocument";
        final String viewName = "view";
        final ViewRequestBuilder viewRequestBuilder = database.getViewRequestBuilder(designDocumentName, viewName);
        final Key.Type<String> keyType = Key.Type.STRING;
        final Class<JsonNull> valueType = JsonNull.class;
        final MultipleRequestBuilder<String, JsonNull> multipleRequestBuilder = viewRequestBuilder.newMultipleRequest(keyType, valueType);
        // Note, there is no call to multipleRequestBuilder.add()
        final ViewMultipleRequest<String, JsonNull> viewMultipleRequest = multipleRequestBuilder.build();
        final List<ViewResponse<String, JsonNull>> viewResponsesList = viewMultipleRequest.getViewResponses();
    }

}

Another question then is in what way to fix this. Should java-cloudant silently accept these zero queries, perform these zero queries (= i.e. no http call to Cloudant), and return an empty result list? Or should an exception be thrown that the query is malformed? Or should Cloudant be queried in every case, returning whatever Cloudant returns (empty result set or error)? Anyways, the current behaviour is broken.

ricellis commented 7 years ago

The limitation is documented

It is only valid to call build() after adding one or more requests. If parameters have been set but the request has not been added then an IllegalStateException will be thrown.

But yes, there is a bug where we should by throwing the IllegalStateException earlier.