web3j和spark的okhttp3包冲突问题解决

最近以太坊的分叉,新增了一些字段,原来使用的是老版本 web3j-spring-boot-starter

<groupId>org.web3j</groupId>
    <artifactId>web3j-spring-boot-starter</artifactId>
<version>1.6.0</version>

现在不得不更新web3j到最新版本包4.8.7,获取一些新的字段信息。

但是由于web3j的版本和spark.2.4.8的版本中okhttp3一直有冲突。

如果是开发spring项目,倒是可以正常使用,但是spark中okhttp3的版本要是升级也报错,最后查到如下方法,可以解决。

 

就是通过打包插件重新打包后修改包名解决,所以该方法也可以用于类似不兼容情况。 

 

1. 新建项目myokhttp,pom文件设置如下:

(这里使用插件maven-shade-plugin的relocation来重新更新包名,不需要写任何程序,只需要添加依赖)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>my_squareup.okhttp3</groupId>
    <artifactId>myokhttp</artifactId>
    <version>1.0.0</version>


    <properties>
        <okhttp.version>4.9.1</okhttp.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>${okhttp.version}</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>logging-interceptor</artifactId>
            <version>${okhttp.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <relocations>
                                <relocation>
                                    <pattern>okhttp3</pattern>
                                    <shadedPattern>myokhttp3</shadedPattern>
                                </relocation>
                            </relocations>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/maven/**</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

2. 打包程序后生成myokhttp-1.0.0.jar包文件,使用jd-gui.exe反编译查看,包名已经修改为myokhttp3开头。

web3j和spark的okhttp3包冲突问题解决

 

 3. 将包更新到我们的maven仓库中。

@echo off
set jarFilePath=D:\tool\batchMaven\jar\myokhttp-1.0.0.jar
set groupId=my_squareup.okhttp3
set artifactId=myokhttp
set version=1.0.0
set s=mvn install:install-file -Dfile=%jarFilePath% -DgroupId=%groupId% -DartifactId=%artifactId% -Dversion=%version% -Dpackaging=jar
echo %s%

call %s%
echo 执行完毕
pause

4. 然后在需要的项目中将web3j中的okhttp3包屏蔽,然后添加我们自定义的包的依赖。

        <dependency>
            <groupId>org.web3j</groupId>
            <artifactId>core</artifactId>
            <version>4.8.7</version>
            <exclusions>
                <exclusion>
                    <groupId>com.squareup.okhttp3</groupId>
                    <artifactId>okhttp</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>my_squareup.okhttp3</groupId>
            <artifactId>myokhttp</artifactId>
            <version>1.0.0</version>
        </dependency>

5. 修改项目中使用到的okhttp3的地方。

1)这里还需要重新写一个HttpServer,从web3j中直接拷贝过来,把引用到okhttp3的地方修改为myokhttp3

/*
 * Copyright 2019 Web3 Labs Ltd.
 *
 * 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 com.my.blockchain.http;

import myokhttp3.*;
import myokhttp3.logging.HttpLoggingInterceptor;
import okio.Buffer;
import okio.BufferedSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.web3j.protocol.Service;
import org.web3j.protocol.exceptions.ClientConnectionException;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static myokhttp3.ConnectionSpec.CLEARTEXT;

/** HTTP implementation of our services API. */
public class HttpService extends Service {

    /** Copied from {@link ConnectionSpec#APPROVED_CIPHER_SUITES}. */
    @SuppressWarnings("JavadocReference")
    private static final CipherSuite[] INFURA_CIPHER_SUITES =
            new CipherSuite[] {
                    CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                    CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                    CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
                    CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
                    CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
                    CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,

                    // Note that the following cipher suites are all on HTTP/2's bad cipher suites list.
                    // We'll
                    // continue to include them until better suites are commonly available. For example,
                    // none
                    // of the better cipher suites listed above shipped with Android 4.4 or Java 7.
                    CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
                    CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
                    CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
                    CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
                    CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
                    CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
                    CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                    CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
                    CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,

                    // Additional INFURA CipherSuites
                    CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
                    CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
                    CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
                    CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256
            };

    private static final ConnectionSpec INFURA_CIPHER_SUITE_SPEC =
            new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
                    .cipherSuites(INFURA_CIPHER_SUITES)
                    .build();

    /** The list of {@link ConnectionSpec} instances used by the connection. */
    private static final List<ConnectionSpec> CONNECTION_SPEC_LIST =
            Arrays.asList(INFURA_CIPHER_SUITE_SPEC, CLEARTEXT);

    public static final MediaType JSON_MEDIA_TYPE =
            MediaType.parse("application/json; charset=utf-8");

    public static final String DEFAULT_URL = "http://localhost:8545/";

    private static final Logger log = LoggerFactory.getLogger(HttpService.class);

    private OkHttpClient httpClient;

    private final String url;

    private final boolean includeRawResponse;

    private HashMap<String, String> headers = new HashMap<>();

    public HttpService(String url, OkHttpClient httpClient, boolean includeRawResponses) {
        super(includeRawResponses);
        this.url = url;
        this.httpClient = httpClient;
        this.includeRawResponse = includeRawResponses;
    }

    public HttpService(OkHttpClient httpClient, boolean includeRawResponses) {
        this(DEFAULT_URL, httpClient, includeRawResponses);
    }

    public HttpService(String url, OkHttpClient httpClient) {
        this(url, httpClient, false);
    }

    public HttpService(String url) {
        this(url, createOkHttpClient());
    }

    public HttpService(String url, boolean includeRawResponse) {
        this(url, createOkHttpClient(), includeRawResponse);
    }

    public HttpService(OkHttpClient httpClient) {
        this(DEFAULT_URL, httpClient);
    }

    public HttpService(boolean includeRawResponse) {
        this(DEFAULT_URL, includeRawResponse);
    }

    public HttpService() {
        this(DEFAULT_URL);
    }

    public static OkHttpClient.Builder getOkHttpClientBuilder() {
        final OkHttpClient.Builder builder =
                new OkHttpClient.Builder().connectionSpecs(CONNECTION_SPEC_LIST);
        configureLogging(builder);
        return builder;
    }

    private static OkHttpClient createOkHttpClient() {
        return getOkHttpClientBuilder().build();
    }

    private static void configureLogging(OkHttpClient.Builder builder) {
        if (log.isDebugEnabled()) {
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor(log::debug);
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);
            builder.addInterceptor(logging);
        }
    }

    @Override
    protected InputStream performIO(String request) throws IOException {

        RequestBody requestBody = RequestBody.create(request, JSON_MEDIA_TYPE);
        Headers headers = buildHeaders();

        myokhttp3.Request httpRequest =
                new myokhttp3.Request.Builder().url(url).headers(headers).post(requestBody).build();

        try (myokhttp3.Response response = httpClient.newCall(httpRequest).execute()) {
            processHeaders(response.headers());
            ResponseBody responseBody = response.body();
            if (response.isSuccessful()) {
                if (responseBody != null) {
                    return buildInputStream(responseBody);
                } else {
                    return null;
                }
            } else {
                int code = response.code();
                String text = responseBody == null ? "N/A" : responseBody.string();

                throw new ClientConnectionException(
                        "Invalid response received: " + code + "; " + text);
            }
        }
    }

    protected void processHeaders(Headers headers) {
        // Default implementation is empty
    }

    private InputStream buildInputStream(ResponseBody responseBody) throws IOException {
        if (includeRawResponse) {
            // we have to buffer the entire input payload, so that after processing
            // it can be re-read and used to populate the rawResponse field.

            BufferedSource source = responseBody.source();
            source.request(Long.MAX_VALUE); // Buffer the entire body
            Buffer buffer = source.getBuffer();

            long size = buffer.size();
            if (size > Integer.MAX_VALUE) {
                throw new UnsupportedOperationException(
                        "Non-integer input buffer size specified: " + size);
            }

            int bufferSize = (int) size;
            InputStream inputStream = responseBody.byteStream();

            BufferedInputStream bufferedinputStream =
                    new BufferedInputStream(inputStream, bufferSize);

            bufferedinputStream.mark(inputStream.available());
            return bufferedinputStream;

        } else {
            return new ByteArrayInputStream(responseBody.bytes());
        }
    }

    private Headers buildHeaders() {
        return Headers.of(headers);
    }

    public void addHeader(String key, String value) {
        headers.put(key, value);
    }

    public void addHeaders(Map<String, String> headersToAdd) {
        headers.putAll(headersToAdd);
    }

    public HashMap<String, String> getHeaders() {
        return headers;
    }

    public String getUrl() {
        return url;
    }

    @Override
    public void close() throws IOException {}
}

2)扩展web3j,新建Web3jExtend (由于web3j的查询功能有限,还需要查询额外的接口,这里做个扩展)

/**
 * @Author: KingWang
 * @Date: 2021/8/16
 * @Desc: 补充web3j没有的接口功能
 **/
public class Web3jExtend extends JsonRpc2_0Web3j implements Web3j {

    public Web3jExtend(Web3jService web3jService) {
        super(web3jService);
    }


    //这里写扩展的功能
    public Request<?, NetVersion> test() {
        return new Request<>(
                "my_test", Collections.<String>emptyList(), web3jService, NetVersion.class);
    }
    
}

3)建立web3j的工具类,工具类中使用的都是myokhttp类 (scala)

/**
 * @Author: KingWang
 * @Date: 2021/7/19  
 * @Desc: web3j的工具类
 **/
object Web3jUtil {

  def getWeb3j:Web3jExtend = {
    val pool: ConnectionPool = new ConnectionPool(500, 5L, TimeUnit.MINUTES)
    val httpClient = new OkHttpClient().newBuilder().connectionPool(pool).build()
    val httpService = new HttpService(APIConstant.ethClientAddress, httpClient, false)
    new Web3jExtend(httpService)
  }

}

 

6. 测试

  def getBlock(blockNumber:String) = {
    val web3j = Web3jUtil.getWeb3j
    val defaultBlockParameter = new DefaultBlockParameterNumber(new BigInteger(blockNumber))
    val ethBlock =web3j.ethGetBlockByNumber(defaultBlockParameter,true).send()
    val block = ethBlock.getBlock
    println(JSON.toJSONString(block,SerializerFeature.WriteMapNullValue))
  }

web3j和spark的okhttp3包冲突问题解决

 

上一篇:python机器学习及实践-第一章


下一篇:day3_25 关于值传递和指针传递