• 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开头。

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

    @echo off
    set jarFilePath=D:	oolatchMavenjarmyokhttp-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))
      }

    作者:尤灯塔
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    Query on The Trees(hdu 4010)
    背单词(bzoj 4567)
    P2819 图的m着色问题
    P1605 迷宫
    P1230 智力大冲浪
    P1082 同余方程
    P3372 【模板】线段树 1
    P2626 斐波那契数列(升级版)
    长生诀
    写给我第一个喜欢的男孩的歌
  • 原文地址:https://www.cnblogs.com/30go/p/15149647.html
Copyright © 2020-2023  润新知