本地tomcat调用远程接口报错:java.lang.reflect.InvocationTargetException

  今天碰到一个奇怪的问题,本地Eclipse起了一个tomcat通过http去调一个外部接口,结果竟然报了一个反射的异常——java.lang.reflect.InvocationTargetException,从日志里看不出啥来,通过eclipse调试发生了诡异的事情——直接跳入异常了

    /**
     * Constructs a InvocationTargetException with a target exception.
     *
     * @param target the target exception
     */
    public InvocationTargetException(Throwable target) {
        super((Throwable)null);  // Disallow initCause
        this.target = target;
    }

  

  从异常看,是程序找不到调用代码了,但是eclipse里面是可以找到调用类和方法的。想了一下,应该是编译出问题了,或许class文件里没有应的代码。去eclipse部署的本地tomcat路径下找class:打开Servers窗口 -> 找到Server Locations,看Server path和Deploy path,进入本地部署路径:

  进入wtpwebapps -> 进入war包 -> 找到引用的jar -> 打开jar包,根据包路径找类,果然没有调用类。估计是之前引用的jar包编译出了问题。

  解决办法:重新编译问题jar包,刷新引用jar包的工程,最后clean一下tomcat,再次进入到tomcat本地部署路径E:\workspace.metadata.pluginsorg.eclipse.wst.server.core mp0wtpwebapps,发现调用类出现了,重新启动tomcat,问题依然存在。这就奇怪了,重新调试,发现另一个问题:

java.lang.NoSuchMethodError: io.netty.handler.ssl.SslContextBuilder.protocols([Ljava/lang/String;)Lio/netty/handler/ssl/SslContextBuilder;

  有点摸不着头脑,代码调试时流程没变,直接跳入异常,但异常显示的是上面的问题,找不到SslContextBuilder.protocols这个方法。

  问题定位:仔细看了下调用类,发现有这么一个方法:

    private static AsyncHttpClient asynHttpClient = getAsyncHttpClient();
    
    /**
     * 获取请求类的客户端
     * 
     * @return
     */
    public static AsyncHttpClient getAsyncHttpClient()
    {
        AsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder().setFollowRedirect(false)
            .setConnectTimeout(PropertiesConfig.getInt("asynHttp.connectTimeout", 500))
            .setRequestTimeout(PropertiesConfig.getInt("asynHttp.requestTimeout", 10000))
            .setReadTimeout(PropertiesConfig.getInt("asynHttp.readTimeout", 10000))
            .build();
        AsyncHttpClient client = new DefaultAsyncHttpClient(config);
        return client;
    }

 

  从上面看出,类一开始就通过静态方法getAsyncHttpClient得到异步请求的客户端对象asynHttpClient,所以应该在getAsyncHttpClient方法加断点,否则就直接进入异常了。异常跟踪直接看日志:

ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console. Set system property 'log4j2.debug' to show Log4j2 internal initialization logging.
Exception in thread "main" java.lang.NoSuchMethodError: io.netty.handler.ssl.SslContextBuilder.protocols([Ljava/lang/String;)Lio/netty/handler/ssl/SslContextBuilder;
    at org.asynchttpclient.netty.ssl.DefaultSslEngineFactory.buildSslContext(DefaultSslEngineFactory.java:45)
    at org.asynchttpclient.netty.ssl.DefaultSslEngineFactory.init(DefaultSslEngineFactory.java:69)
    at org.asynchttpclient.netty.channel.ChannelManager.<init>(ChannelManager.java:110)
    at org.asynchttpclient.DefaultAsyncHttpClient.<init>(DefaultAsyncHttpClient.java:85)

  

  一路跟过去:

  DefaultAsyncHttpClient:

     /**
     * Create a new HTTP Asynchronous Client using the specified
     * {@link DefaultAsyncHttpClientConfig} configuration. This configuration
     * will be passed to the default {@link AsyncHttpClient} that will be
     * selected based on the classpath configuration.
     *
     * @param config a {@link DefaultAsyncHttpClientConfig}
     */
    public DefaultAsyncHttpClient(AsyncHttpClientConfig config) {

        this.config = config;

        allowStopNettyTimer = config.getNettyTimer() == null;
        nettyTimer = allowStopNettyTimer ? newNettyTimer() : config.getNettyTimer();

        channelManager = new ChannelManager(config, nettyTimer);
        requestSender = new NettyRequestSender(config, channelManager, nettyTimer, new AsyncHttpClientState(closed));
        channelManager.configureBootstraps(requestSender);
    }

 

  ChannelManager:

 
    public ChannelManager(final AsyncHttpClientConfig config, Timer nettyTimer) {

        this.config = config;
        this.sslEngineFactory = config.getSslEngineFactory() != null ? config.getSslEngineFactory() : new DefaultSslEngineFactory();
        try {
            this.sslEngineFactory.init(config);
        } catch (SSLException e) {
            throw new RuntimeException("Could not initialize sslEngineFactory", e);
        }

   ...
 

 

  DefaultSslEngineFactory:

 
package org.asynchttpclient.netty.ssl;

import io.netty.buffer.ByteBufAllocator;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import java.util.Arrays;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import org.asynchttpclient.AsyncHttpClientConfig;
import org.asynchttpclient.util.MiscUtils;

public class DefaultSslEngineFactory
  extends SslEngineFactoryBase
{
  private volatile SslContext sslContext;
  
  private SslContext buildSslContext(AsyncHttpClientConfig config)
    throws SSLException
  {
    if (config.getSslContext() != null) {
      return config.getSslContext();
    }
    SslContextBuilder sslContextBuilder = SslContextBuilder.forClient().sslProvider(config.isUseOpenSsl() ? SslProvider.OPENSSL : SslProvider.JDK).sessionCacheSize(config.getSslSessionCacheSize()).sessionTimeout(config.getSslSessionTimeout());
    if (MiscUtils.isNonEmpty(config.getEnabledProtocols())) {
      sslContextBuilder.protocols(config.getEnabledProtocols());
    }
    if (MiscUtils.isNonEmpty(config.getEnabledCipherSuites())) {
      sslContextBuilder.ciphers(Arrays.asList(config.getEnabledCipherSuites()));
    }
    if (config.isUseInsecureTrustManager()) {
      sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
    }
    return configureSslContextBuilder(sslContextBuilder).build();
  }
  
 ...
 

 

  终于找到正在——DefaultSslEngineFactory调用SslContextBuilder.protocols方法时找不到了,进入SslContextBuilder类看了一眼,确实没有protocols方法。去看了下pomx.ml文件,引入的async-http-client.jar是2.1.0-alpha26版本,匹配的netty-all.jar却是4.1.8.Final版本的,而这个版本的SslContextBuilder并不存在protocols方法。

 

  问题解决:使用2.1.0-alpha6版本async-http-client的jar包,这个包里DefaultSslEngineFactory没有调用SslContextBuilder.protocols方法:

 
package org.asynchttpclient.netty.ssl;

import io.netty.buffer.ByteBufAllocator;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;

import org.asynchttpclient.AsyncHttpClientConfig;

public class DefaultSslEngineFactory extends SslEngineFactoryBase {

    private volatile SslContext sslContext;

    private SslContext buildSslContext(AsyncHttpClientConfig config) throws SSLException {
        if (config.getSslContext() != null)
            return config.getSslContext();

        SslContextBuilder sslContextBuilder = SslContextBuilder.forClient()//
                .sslProvider(config.isUseOpenSsl() ? SslProvider.OPENSSL : SslProvider.JDK)//
                .sessionCacheSize(config.getSslSessionCacheSize())//
                .sessionTimeout(config.getSslSessionTimeout());

        if (config.isUseInsecureTrustManager()) {
            sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
        }

        return configureSslContextBuilder(sslContextBuilder).build();
    }
  
  ...
}

  重启后测试通过,问题解决。

posted on 2018-04-12 16:26  不想下火车的人  阅读(8265)  评论(0编辑  收藏  举报

导航