使用嵌入式 tomcat 服务器进行 JUnit 测试,如何为 http 和 https 连接器指定自动端口?
Posted
技术标签:
【中文标题】使用嵌入式 tomcat 服务器进行 JUnit 测试,如何为 http 和 https 连接器指定自动端口?【英文标题】:JUnit test with Embedded tomcat server , how to specify automatic ports for both http and https connectors? 【发布时间】:2017-07-07 00:47:46 【问题描述】:说明
我做了一个 JUnit 测试,专注于尝试测试对 SOAP Web 服务的调用。
我正在使用嵌入式 tomcat 服务器进行测试,以便使用模拟服务器运行我的测试。
我也同时使用 http 和 https 连接器。
我需要为这两个连接器使用自动端口,因为测试在 Jenkins 服务器上运行,我不能只使用端口 443 或 8443,因为它们已经被占用了。
我知道将端口 0 用作标准端口会导致 tomcat 使用自动端口分配,但我无法设法将它与两个连接器一起使用。
预期行为
我还想为我的自定义 ssl 连接器使用自动端口分配。
有没有可能以某种方式做到这一点?
示例代码
这是我的 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();
这里是我的自定义 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;
【问题讨论】:
当您说“但我无法将它与两个连接器一起使用”时,您能澄清一下您到底遇到了什么麻烦吗?你有什么例外吗?有什么不工作? 为简化起见,我希望能够为 http 和 https 连接器选择自动端口,但我无法在 setRedirectPort() 方法中指定端口 0。跨度> 【参考方案1】:我没试过,但从代码看起来是这样的
服务器启动后可以setRedirectPort
您可以使用Connector.getLocalPort
获取实际端口
所以我认为你可以尝试添加类似的东西
mTomcat.start(); // <-- your existing code
defaultConnector.setRedirectPort(SSLConnector.getLocalPort())
【讨论】:
【参考方案2】:我有两个解决这个问题的方法:
手动选择 SSL 端口
ServerSocket(0) constructor 自动选择一个空闲端口。 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();
我知道,有可能另一个进程在testSocket.close()
和tomcat.start()
之间分配了相同的端口,但是您可以使用LifecycleState.FAILED.equals(sslConnector.getState())
测试来检测这种情况。
使用生命周期监听器
Tomcat 连接器具有生命周期感知能力,因此您会收到有关“before_init”和“after_init”事件的通知。 Tomcat 按照您将连接器添加到服务的顺序初始化它们。
-
添加 ssl 连接器。
添加一个 http 连接器。 (这将是“默认”连接器。不要调用
mTomcat.getConnector()
,因为它会获取第一个连接器或创建新连接器。)
当 ssl 连接器初始化完成后,您可以通过getLocalPort() 调用获取所选端口。
在http连接器初始化之前,调用setRedirectPort
完整示例:
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();
【讨论】:
以上是关于使用嵌入式 tomcat 服务器进行 JUnit 测试,如何为 http 和 https 连接器指定自动端口?的主要内容,如果未能解决你的问题,请参考以下文章
将嵌入式 Tomcat 从 v6 更改为 v7 会导致 InitialContext 查找失败
在嵌入式 Jetty 上使用 javax.websocket 进行 JUnit 测试抛出 RejectedExecutionException: NonBlockingThread