org.ScripterRon.BitcoinMonitor.Request.java Source code

Java tutorial

Introduction

Here is the source code for org.ScripterRon.BitcoinMonitor.Request.java

Source

/*
 * Copyright 2014-2015 Ronald Hoffman.
 *
 * 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 org.ScripterRon.BitcoinMonitor;

import static org.ScripterRon.BitcoinMonitor.Main.log;

import org.json.simple.JSONArray;
import org.json.simple.parser.ContainerFactory;
import org.json.simple.parser.JSONParser;

import java.io.FilterOutputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.atomic.AtomicLong;
import java.util.List;
import java.util.Map;

/**
 * Make API requests to the Bitcoin node and return the response
 */
public class Request {

    /** Response container factory */
    private static final ContainerFactory containerFactory = new ResponseFactory();

    /** Connect timeout (milliseconds) */
    private static final int nodeConnectTimeout = 5000;

    /** Read timeout (milliseconds) */
    private static final int nodeReadTimeout = 30000;

    /** Request identifier */
    private static final AtomicLong requestId = new AtomicLong(0);

    /** Set up our default authenticator */
    static {
        Authenticator.setDefault(new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(Main.rpcUser, Main.rpcPassword.toCharArray());
            }
        });
    }

    /**
     * Get the block for the specified block hash
     *
     * @param       blockHash               Block hash
     * @return                              Block
     * @throws      IOException             Unable to issue Bitcoin RPC request
     * @throws      ParseException          Unable to parse the Bitcoin RPC response
     */
    public static Response getBlock(String blockHash) throws IOException, ParseException {
        Response response = issueRequest("getblock", String.format("[\"%s\"]", blockHash));
        Object result = response.get("result");
        if (result == null || !(result instanceof Response)) {
            log.error("'getblock' result is not a JSONObject");
            throw new ParseException("'getblock' result is not a JSONObject");
        }
        return (Response) result;
    }

    /**
     * Get the block hash for the specified block chain height
     *
     * @param       height                  Block chain height
     * @return                              Block hash
     * @throws      IOException             Unable to issue Bitcoin RPC request
     * @throws      ParseException          Unable to parse the Bitcoin RPC response
     */
    public static String getBlockHash(int height) throws IOException, ParseException {
        Response response = issueRequest("getblockhash", String.format("[%d]", height));
        Object result = response.get("result");
        if (result == null || !(result instanceof String)) {
            log.error("'getblockhash' result is not a String");
            throw new ParseException("'getblockhash' result is not a String");
        }
        return (String) result;
    }

    /**
     * Get the server log messages using the getlog method
     *
     * @return                              Log message list
     * @throws      IOException             Unable to issue Bitcoin RPC request
     * @throws      ParseException          Unable to parse the Bitcoin RPC response
     */
    public static List getLog() throws IOException, ParseException {
        Response response = issueRequest("getlog", null);
        Object result = response.get("result");
        if (result == null || !(result instanceof JSONArray)) {
            log.error("'getlog' result is not a JSONArray");
            throw new ParseException("'getlog' result is not a JSONArray");
        }
        return (List) result;
    }

    /**
     * Get node information using the getinfo method
     *
     * @return                              Node information
     * @throws      IOException             Unable to issue Bitcoin RPC request
     * @throws      ParseException          Unable to parse the Bitcoin RPC response
     */
    public static Response getNodeInfo() throws IOException, ParseException {
        Response response = issueRequest("getinfo", null);
        Object result = response.get("result");
        if (result == null || !(result instanceof Response)) {
            log.error("'getinfo' result is not a JSONObject");
            throw new ParseException("'getinfo' result is not a JSONObject");
        }
        return (Response) result;
    }

    /**
     * Get peer information using the getpeerinfo method
     *
     * @return                              Peer information
     * @throws      IOException             Unable to issue Bitcoin RPC request
     * @throws      ParseException          Unable to parse the Bitcoin RPC response
     */
    public static List getPeerInfo() throws IOException, ParseException {
        Response response = issueRequest("getpeerinfo", null);
        Object result = response.get("result");
        if (result == null || !(result instanceof JSONArray)) {
            log.error("'getperrinfo' result is not a JSONArray");
            throw new ParseException("'getperrinfo' result is not a JSONArray");
        }
        return (List) result;
    }

    /**
     * Get the server stack traces using the getstacktraces method
     *
     * @return                              Server stack traces
     * @throws      IOException             Unable to issue Bitcoin RPC request
     * @throws      ParseException          Unable to parse the Bitcoin RPC response
     */
    public static StackTraces getStackTraces() throws IOException, ParseException {
        Response response = issueRequest("getstacktraces", null);
        Object result = response.get("result");
        if (result == null || !(result instanceof Response)) {
            log.error("'getstacktraces' result is not a JSONObject");
            throw new ParseException("'getstacktraces' result is not a JSONObject");
        }
        return new StackTraces((Response) result);
    }

    /**
     * Issue the Bitcoin RPC request and return the parsed JSON response
     *
     * @param       requestType             Request type
     * @param       requestParams           Request parameters in JSON format or null if no parameters
     * @return                              Parsed JSON response
     * @throws      IOException             Unable to issue Bitcoin RPC request
     * @throws      ParseException          Unable to parse the Bitcoin RPC response
     */
    private static Response issueRequest(String requestType, String requestParams)
            throws IOException, ParseException {
        long id = requestId.incrementAndGet();
        Response response = null;
        try {
            URL url = new URL(String.format("http://%s:%d/", Main.rpcHost, Main.rpcPort));
            String request;
            if (requestParams != null) {
                request = String.format("{\"jsonrpc\": \"2.0\", \"method\": \"%s\", \"params\": %s, \"id\": %d}",
                        requestType, requestParams, id);
            } else {
                request = String.format("{\"jsonrpc\": \"2.0\", \"method\": \"%s\", \"id\": %d}", requestType, id);
            }
            log.debug(String.format("Issue HTTP request to %s:%d: %s", Main.rpcHost, Main.rpcPort, request));
            byte[] requestBytes = request.getBytes("UTF-8");
            //
            // Issue the request
            //
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "application/json-rpc");
            conn.setRequestProperty("Cache-Control", "no-cache, no-store");
            conn.setRequestProperty("Content-Length", String.format("%d", requestBytes.length));
            conn.setRequestProperty("Accept", "application/json-rpc");
            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setUseCaches(false);
            conn.setConnectTimeout(nodeConnectTimeout);
            conn.setReadTimeout(nodeReadTimeout);
            conn.connect();
            try (FilterOutputStream out = new FilterOutputStream(conn.getOutputStream())) {
                out.write(requestBytes);
                out.flush();
                int code = conn.getResponseCode();
                if (code != HttpURLConnection.HTTP_OK) {
                    String errorText = String.format("Response code %d for %s request\n  %s", code, requestType,
                            conn.getResponseMessage());
                    log.error(errorText);
                    throw new IOException(errorText);
                }
            }
            //
            // Parse the response
            //
            try (InputStreamReader in = new InputStreamReader(conn.getInputStream(), "UTF-8")) {
                JSONParser parser = new JSONParser();
                response = (Response) parser.parse(in, containerFactory);
                Response errorResponse = (Response) response.getObject("error");
                if (errorResponse != null) {
                    String errorText = String.format("Error %d returned for %s request\n  %s",
                            errorResponse.getInt("code"), requestType, errorResponse.getString("message"));
                    log.error(errorText);
                    throw new IOException(errorText);
                }
            }
            if (log.isDebugEnabled())
                log.debug(String.format("Request complete\n%s", Utils.formatJSON(response)));
        } catch (MalformedURLException exc) {
            throw new IOException("Malformed Bitcoin RPC URL", exc);
        } catch (org.json.simple.parser.ParseException exc) {
            String errorText = String.format("JSON parse exception for %s request: Position %d, Code %d",
                    requestType, exc.getPosition(), exc.getErrorType());
            log.error(errorText);
            throw new ParseException(errorText, exc.getPosition(), exc.getErrorType());
        } catch (IOException exc) {
            String errorText = String.format("I/O error on %s request", requestType);
            log.error(errorText, exc);
            throw new IOException(errorText);
        }
        return response;
    }

    /**
     * JSON container factory
     *
     * We will create Response for an object and JSONArray<Object> for an array
     */
    private static class ResponseFactory implements ContainerFactory {

        /**
         * Create an object container
         *
         * @return                          PeerResponse
         */
        @Override
        public Map createObjectContainer() {
            return new Response();
        }

        /**
         * Create an array container
         *
         * @return                          JSONArray
         */
        @Override
        public List<Object> creatArrayContainer() {
            return new JSONArray();
        }
    }
}