简体   繁体   English

使用嵌入式tomcat服务器进行JUnit测试,如何为http和https连接器指定自动端口?

[英]JUnit test with Embedded tomcat server , how to specify automatic ports for both http and https connectors?

Description 描述

I have made a JUnit test that focus on trying to test a call to a SOAP web service. 我做了一个JUnit测试,专注于尝试测试对SOAP Web服务的调用。

I am using an embedded tomcat server for my test in order to run my test with a mock server. 我正在使用嵌入式tomcat服务器进行测试,以便使用模拟服务器运行我的测试。

I am also using both http and https connectors. 我也使用http和https连接器。

I need to use automatic ports for both these connectors because the test is running on a Jenkins server and i can't just use port 443 or 8443 as they are already taken. 我需要为这两个连接器使用自动端口,因为测试在Jenkins服务器上运行,我不能只使用端口443或8443,因为它们已被占用。

I understand that using the port 0 as standard port will result in tomcat using automatic port allocation but I can't manage to use it with both connectors. 我知道使用端口0作为标准端口将导致tomcat使用自动端口分配,但我无法使用它与两个连接器。

Expected behavior 预期的行为

I'd like to use automatic port allocation also for my custom ssl connector. 我也想为我的自定义ssl连接器使用自动端口分配。

Is it possible to do so in some way ? 是否有可能以某种方式这样做?

Sample code 示例代码

Here is the code for my tomcat instance : 这是我的tomcat实例的代码:

@Before
public void setup() throws Throwable {

    File tomcatWorkingDir = new File(mWorkingDir);

    //Empty the target/tomcat-working-dir directory if it exist
    //Create the directory otherwise
    if(tomcatWorkingDir.exists() && tomcatWorkingDir.isDirectory()){
        LOGGER.info("cleaning tomcat-working-dir directory");
        FileUtils.cleanDirectory(new File(mWorkingDir)); 
    } else {
        LOGGER.info("create tomcat-working-dir directory");
        tomcatWorkingDir.mkdir();
    }

    LOGGER.info("disabling ssl certification validation");
    //Disable JVM ssl sockets connection
    disableJVMCertificate();

    //Add server certificate
    createServerCertificate();

    //Custom SSL Connector
    Connector SSLConnector = getSSLConnector();

    mTomcat = new Tomcat();

    //Standard http startup port
    mTomcat.setPort(0);

    //Set up base directory 
    //Otherwise, tomcat would use the current directory
    mTomcat.setBaseDir(mWorkingDir);

    LOGGER.info("setting the ssl connector in TOMCAT");
    Service service = mTomcat.getService();
    service.addConnector(SSLConnector);

    //Redirect current port
    Connector defaultConnector = mTomcat.getConnector();
    defaultConnector.setRedirectPort(SERVER_HTTPS_PORT);

    //Configure the way WAR are managed by the engine
    mTomcat.getHost().setAutoDeploy(true);
    mTomcat.getHost().setDeployOnStartup(true);

    //Add mock server into our webApp
    String servletName = "/server";
    File webApp = new File(mWorkingDir,"../../../ws-mock-server/src/main/webapp");

    mTomcat.addWebapp(mTomcat.getHost(), servletName, webApp.getAbsolutePath());

    //start tomcat
    LOGGER.info("starting TOMCAT");

    mTomcat.start();
  }

and here for my custom ssl connector. 这里是我的自定义ssl连接器。

    private static Connector getSSLConnector(){
    Connector connector = new Connector();
    connector.setPort(SERVER_HTTPS_PORT);
    connector.setSecure(true);

    //Http protocol Http11AprProtocol
    connector.setAttribute("protocol", "org.apache.coyote.http11.Http11AprProtocol");

    //Maximum threads allowedd on this instance of tomcat
    connector.setAttribute("maxThreads","200");
    connector.setAttribute("SSLEnabled", true);

    //No client Authentification is required in order to connect
    connector.setAttribute("clientAuth", false);

    //SSL TLSv1 protocol
    connector.setAttribute("sslProtocol","TLS");

    //Ciphers configuration describing how server will encrypt his messages
    //A common cipher suite need to exist between server and client in an ssl
    //communication in order for the handshake to succeed
    connector.setAttribute("ciphers","TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");

    LOGGER.info("setting keystore file");
    //Here an absolute file path is needed in order to properly set up the keystore attribute
    connector.setAttribute("keystoreFile",new File(".").getAbsolutePath().replace("\\", "/")+"/"+mWorkingDir+"/server.jks");

    LOGGER.info("setting keystore pass");
    connector.setAttribute("keystorePass","changeit");

    return connector;
}

I have two solutions for this problem: 我有两个解决这个问题的方法:

Select SSL port manually 手动选择SSL端口

The ServerSocket(0) constructor automatically selects a free port. ServerSocket(0)构造函数自动选择一个空闲端口。 The Tomcat uses this method also. Tomcat也使用这种方法。

try (ServerSocket testSocket = new ServerSocket(0)) {
    int randomFreePort = testSocket.getLocalPort(); 
    sslConnector.setPort(randomFreePort);
    defaultConnector.setRedirectPort( randomFreePort);
} // At this point the testSocket.close() called
tomcat.start();

I know, there is a probability, that an another process allocates the same port between the testSocket.close() and tomcat.start() , but you can detect this situation, with LifecycleState.FAILED.equals(sslConnector.getState()) test. 我知道,有一个概率,另一个进程在testSocket.close()tomcat.start()之间分配相同的端口,但你可以使用LifecycleState.FAILED.equals(sslConnector.getState())来检测这种情况。测试。

Use lifecycle listeners 使用生命周期监听器

Tomcat connectors are lifecycle aware, so you will be notified on 'before_init' and 'after_init' events. Tomcat连接器具有生命周期感知功能,因此您将收到有关'before_init'和'after_init'事件的通知。 Tomcat initializes the connectors in the order as you added them to the Service. 当您将连接器添加到服务时,Tomcat会按顺序初始化连接器。

  1. Add the ssl connector. 添加ssl连接器。
  2. Add an http connector. 添加http连接器。 (That will be the 'default' connector. Don't call the mTomcat.getConnector() because it gets the first or creates a new connector. ) (这将是'默认'连接器。不要调用mTomcat.getConnector()因为它获取第一个连接器或创建新连接器。)
  3. When the ssl connector initialization complete, you can get the chosen port with getLocalPort() call. 当ssl连接器初始化完成后,您可以使用getLocalPort()调用获取所选端口。
  4. Before the http connector initialization, call the setRedirectPort 在http连接器初始化之前,调用setRedirectPort

Full example: 完整示例:

    Tomcat mTomcat = new Tomcat();
    Connector sslConnector = getSSLConnector(); 
    mTomcat.getService().addConnector(sslConnector);    
    Connector defaultConnector = new Connector();
    defaultConnector.setPort(0);
    mTomcat.getService().addConnector(defaultConnector);

    // Do the rest of the Tomcat setup

    AtomicInteger sslPort = new AtomicInteger();
    sslConnector.addLifecycleListener(event->{
        if( "after_init".equals(event.getType()) )
            sslPort.set(sslConnector.getLocalPort());
    });
    defaultConnector.addLifecycleListener(event->{
        if( "before_init".equals(event.getType()) )
            defaultConnector.setRedirectPort(sslPort.get());
    });

    mTomcat.start();

I haven't tried it but from the code it looks like 我没有尝试过,但从它看起来的代码

  1. You can setRedirectPort after server was started 您可以在服务器启动后setRedirectPort

  2. You can use Connector.getLocalPort to get actual port 您可以使用Connector.getLocalPort来获取实际端口

So I think you could try to add something like 所以我认为你可以尝试添加类似的东西

mTomcat.start(); // <-- your existing code
defaultConnector.setRedirectPort(SSLConnector.getLocalPort())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM