io.macgyver.neorx.rest.NeoRxClient.java Source code

Java tutorial

Introduction

Here is the source code for io.macgyver.neorx.rest.NeoRxClient.java

Source

/**
 * 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 io.macgyver.neorx.rest;

import static io.macgyver.neorx.rest.impl.GuavaPreconditions.checkArgument;
import static io.macgyver.neorx.rest.impl.GuavaPreconditions.checkNotNull;
import io.macgyver.neorx.rest.impl.GuavaStrings;
import io.macgyver.neorx.rest.impl.NonStreamingResultImpl;
import io.macgyver.neorx.rest.impl.ResultMetaDataImpl;
import io.macgyver.neorx.rest.impl.SslTrust;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import rx.Observable;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.squareup.okhttp.Credentials;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Request.Builder;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;

public class NeoRxClient {

    public static final boolean CERTIFICATE_VALIDATION_DEFAULT = true;
    public static final String DEFAULT_URL = "http://localhost:7474";
    private String url = DEFAULT_URL;
    private String username = null;
    private String password = null;
    private boolean validateCertificates = CERTIFICATE_VALIDATION_DEFAULT;
    private boolean streamResponse = true;
    final static ObjectMapper mapper = new ObjectMapper();
    private volatile OkHttpClient httpClient = null;
    Logger logger = LoggerFactory.getLogger(NeoRxClient.class);

    private final Level REQUEST_RESPONSE_LEVEL = Level.FINE; // level at which
    // request/response
    // logging will
    // be logged

    public NeoRxClient() {
        this(DEFAULT_URL);
    }

    public NeoRxClient(String url) {
        this(url, null, null, CERTIFICATE_VALIDATION_DEFAULT);
    }

    public NeoRxClient(String url, boolean validateCertificates) {
        this(url, null, null, validateCertificates);
    }

    public NeoRxClient(String url, String username, String password) {
        this(url, username, password, CERTIFICATE_VALIDATION_DEFAULT);
    }

    public NeoRxClient(String url, String username, String password, boolean validateCertificates) {

        while (url.endsWith("/")) {
            url = url.substring(0, url.length() - 1);
        }
        this.url = url;
        this.username = username;
        this.password = password;
        this.validateCertificates = validateCertificates;

        OkHttpClient client = new OkHttpClient();

        if (!validateCertificates) {
            client.setHostnameVerifier(SslTrust.withoutHostnameVerification());
            client.setSslSocketFactory(SslTrust.withoutCertificateValidation().getSocketFactory());

        }

        this.httpClient = client;
    }

    protected OkHttpClient getClient() {

        return httpClient;
    }

    protected ObjectNode createParameters(Object... args) {
        checkNotNull(args);
        checkArgument(args.length % 2 == 0, "must be an even number of arguments (key/value pairs)");
        ObjectNode n = mapper.createObjectNode();
        for (int i = 0; i < args.length; i += 2) {
            String key = args[i].toString();

            Object val = args[i + 1];
            if (val == null) {
                n.set(key, NullNode.getInstance());
            } else if (val instanceof String) {
                n.put(key, val.toString());
            } else if (val instanceof Integer) {
                n.put(key, (Integer) val);
            } else if (val instanceof Long) {
                n.put(key, (Long) val);
            } else if (val instanceof Double) {
                n.put(key, (Double) val);
            } else if (val instanceof Boolean) {
                n.put(key, (Boolean) val);
            } else if (val instanceof List) {

                ArrayNode an = mapper.createArrayNode();

                for (Object item : (List) val) {
                    an.add(item.toString());
                }
                n.set(key, an);
            } else {
                throw new IllegalArgumentException(
                        "parameter '" + key + "' type not supported: " + val.getClass().getName());
            }

        }
        return n;
    }

    public Observable<JsonNode> execCypher(String cypher, ObjectNode params) {
        ObjectNode response = execRawCypher(cypher, params);
        io.macgyver.neorx.rest.impl.GuavaPreconditions.checkNotNull(response);
        NonStreamingResultImpl r = new NonStreamingResultImpl(response);
        ResultMetaDataImpl md = r.getResultMetaData();
        if (md.getFieldNames().size() == 1) {

            return r.rows().flatMap(

                    NeoRxFunctions.extractField(md.getFieldNames().get(0)));
        } else {
            return r.rows();
        }

    }

    /**
     * Convenience method for returning neo4j results as a List<JsonNode>. Same
     * as: client.execCypher(s,params).toList().toBlocking().first()
     * 
     * @param cypher
     * @param params
     * @return List of JsonNode
     */
    public List<JsonNode> execCypherAsList(String cypher, Object... params) {
        return execCypherAsList(cypher, createParameters(params));
    }

    /**
     * Convenience method for returning neo4j results as a List<JsonNode>. Same
     * as: client.execCypher(s,params).toList().toBlocking().first()
     * 
     * @param cypher
     * @param params
     * @return List of JsonNode
     */
    public List<JsonNode> execCypherAsList(String cypher, ObjectNode n) {
        return execCypher(cypher, n).toList().toBlocking().first();
    }

    public Observable<JsonNode> execCypher(String cypher, Object... params) {
        return execCypher(cypher, createParameters(params));
    }

    protected ObjectNode execRawCypher(String cypher, Object... args) {
        return execRawCypher(cypher, createParameters(args));
    }

    protected ObjectNode formatPayload(String cypher, ObjectNode params) {
        io.macgyver.neorx.rest.impl.GuavaPreconditions.checkNotNull(cypher);
        ObjectNode payload = mapper.createObjectNode();
        if (params == null) {
            params = mapper.createObjectNode();
        }

        ArrayNode statements = mapper.createArrayNode();

        ObjectNode statement = mapper.createObjectNode();

        statement.put("statement", cypher);
        statement.set("parameters", params);
        statements.add(statement);
        payload.set("statements", statements);
        return payload;
    }

    private String newRequestId() {
        return UUID.randomUUID().toString();
    }

    protected ObjectNode execRawCypher(String cypher, ObjectNode params) {

        try {

            ObjectNode payload = formatPayload(cypher, params);

            String payloadString = payload.toString();
            OkHttpClient c = getClient();
            checkNotNull(c);
            String requestId = newRequestId();
            if (logger.isDebugEnabled()) {
                logger.debug(String.format("request[%s]: %s", requestId, payloadString));
            }
            Builder builder = injectCredentials(new Request.Builder())
                    .addHeader("X-Stream", Boolean.toString(streamResponse)).addHeader("Accept", "application/json")
                    .url(getUrl() + "/db/data/transaction/commit")
                    .post(RequestBody.create(MediaType.parse("application/json"), payloadString));

            if (!GuavaStrings.isNullOrEmpty(username) && !GuavaStrings.isNullOrEmpty(password)) {
                builder = builder.addHeader("Authorization", Credentials.basic(username, password));
            }

            com.squareup.okhttp.Response r = c.newCall(builder.build()).execute();

            ObjectNode jsonResponse = (ObjectNode) mapper.readTree(r.body().charStream());
            if (logger.isDebugEnabled()) {
                logger.debug(String.format("response[%s]: %s", requestId, jsonResponse.toString()));
            }
            ObjectNode n = jsonResponse;
            JsonNode error = n.path("errors").path(0);

            if (error instanceof ObjectNode) {

                throw new NeoRxException(((ObjectNode) error));
            }

            return n;

        } catch (IOException e) {
            throw new NeoRxException(e);
        }
    }

    public boolean isCeritificateValidationEnabled() {
        return validateCertificates;
    }

    public String getUrl() {
        return url;
    }

    protected Request.Builder injectCredentials(Request.Builder builder) {
        if (!GuavaStrings.isNullOrEmpty(username) && !GuavaStrings.isNullOrEmpty(password)) {
            return builder.addHeader("Authorization", Credentials.basic(username, password));
        } else {
            return builder;
        }
    }

    public boolean checkConnection() {
        try {

            Response r = getClient()
                    .newCall(injectCredentials(new Request.Builder()).url(getUrl() + "/db/data/").build())
                    .execute();

            if (r.isSuccessful()) {

                r.body().close();
                return true;
            }

        } catch (IOException | RuntimeException e) {

            logger.warn(e.toString());

        }
        return false;
    }
}