com.sulacosoft.bitcoindconnector4j.BitcoindApiHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.sulacosoft.bitcoindconnector4j.BitcoindApiHandler.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.sulacosoft.bitcoindconnector4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpHost;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import com.sulacosoft.bitcoindconnector4j.core.BitcoindException;
import com.sulacosoft.bitcoindconnector4j.core.HttpException;

import net.sf.ezmorph.bean.MorphDynaBean;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;

/**
 *  @author Sebastian Dziak {@literal (sebastian.dziak@sulacosoft.com)}
 */
public class BitcoindApiHandler implements InvocationHandler {

    private static final String CHARACTER_ENCODING = "UTF-8";

    private String uri;
    private CloseableHttpClient httpClient;
    private HttpHost targetHost;
    private HttpClientContext context;
    private AtomicLong id = new AtomicLong(1L);

    public BitcoindApiHandler(String host, int port, String protocol, String uri, String username,
            String password) {
        this.uri = uri;

        httpClient = HttpClients.createDefault();
        targetHost = new HttpHost(host, port, protocol);
        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()),
                new UsernamePasswordCredentials(username, password));

        AuthCache authCache = new BasicAuthCache();
        BasicScheme basicAuth = new BasicScheme();
        authCache.put(targetHost, basicAuth);

        context = HttpClientContext.create();
        context.setCredentialsProvider(credsProvider);
        context.setAuthCache(authCache);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String jsonRequest = String.format(
                "{\"jsonrpc\": \"2.0\", \"method\": \"%s\", \"params\": [%s], \"id\": %s}", method.getName(),
                buildParamsString(args), id.getAndIncrement());

        HttpPost httpPost = new HttpPost(uri);
        httpPost.setEntity(new ByteArrayEntity(jsonRequest.getBytes(CHARACTER_ENCODING)));
        CloseableHttpResponse response = httpClient.execute(targetHost, httpPost, context);
        try {

            checkHttpErrors(response.getStatusLine().getStatusCode());

            String jsonResponse = IOUtils.toString(response.getEntity().getContent(), CHARACTER_ENCODING);
            JSONObject jsonObject = JSONObject.fromObject(jsonResponse);
            MorphDynaBean baseResponse = (MorphDynaBean) JSONObject.toBean(jsonObject);

            checkBitcoindErrors((MorphDynaBean) baseResponse.get("error"));

            return deserializeResult(method, baseResponse.get("result"));

        } finally {
            response.close();
        }
    }

    private String buildParamsString(Object[] args) {
        StringBuilder params = new StringBuilder();
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                if (i > 0)
                    params.append(",");
                Object arg = args[i];
                if (arg instanceof String)
                    params.append(String.format("\"%s\"", arg));
                else
                    params.append(String.format("%s", arg));
            }
        }
        return params.toString();
    }

    private void checkHttpErrors(int statusCode) throws HttpException {
        if (statusCode != HttpStatus.SC_OK && statusCode != HttpStatus.SC_INTERNAL_SERVER_ERROR) {
            if (statusCode == HttpStatus.SC_UNAUTHORIZED)
                throw new HttpException(String.format(
                        "Bitcoind JSON-RPC HTTP error (Probably an incorrect username or password). HTTP Status-Code %s",
                        statusCode), statusCode);
            else
                throw new HttpException(
                        String.format("Bitcoind JSON-RPC HTTP error. HTTP Status-Code %s", statusCode), statusCode);
        }
    }

    private void checkBitcoindErrors(MorphDynaBean jsonError) throws BitcoindException {
        if (jsonError != null) {
            com.sulacosoft.bitcoindconnector4j.response.Error error = (com.sulacosoft.bitcoindconnector4j.response.Error) JSONObject
                    .toBean(JSONObject.fromObject(jsonError),
                            com.sulacosoft.bitcoindconnector4j.response.Error.class);
            throw new BitcoindException(error.getMessage(), error.getCode());
        }
    }

    private Object deserializeResult(Method method, Object result) {
        if (result instanceof MorphDynaBean) {
            return JSONObject.toBean(JSONObject.fromObject(JSONSerializer.toJSON((MorphDynaBean) result)),
                    method.getReturnType());
        } else if (result instanceof List) {
            List<?> incomingList = (List<?>) result;
            if (incomingList.size() == 0)
                return Collections.EMPTY_LIST;

            ParameterizedType type = (ParameterizedType) method.getGenericReturnType(); // method.getGenericType();
            Class<?> clazz = (Class<?>) type.getActualTypeArguments()[0];

            ArrayList<Object> outcomingList = new ArrayList<>();
            if (incomingList.get(0) instanceof MorphDynaBean) {
                for (Object entity : incomingList) {
                    Object ent = JSONObject.toBean(JSONObject.fromObject(entity), clazz);
                    outcomingList.add(ent);
                }
            } else {
                outcomingList.addAll(incomingList);
            }
            return outcomingList;
        } else {
            return result;
        }
    }

}