Merge pull request #2646 from tkyjovsk/performance-tests
Added module 'adapters/jboss/remote' with performance tests.
This commit is contained in:
commit
a44add29b4
46 changed files with 5539 additions and 23 deletions
|
@ -78,6 +78,11 @@
|
|||
<artifactId>wildfly-arquillian-container-managed</artifactId>
|
||||
<version>${arquillian-wildfly-container.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.wildfly</groupId>
|
||||
<artifactId>wildfly-arquillian-container-remote</artifactId>
|
||||
<version>${arquillian-wildfly-container.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
@ -100,9 +105,9 @@
|
|||
</build>
|
||||
|
||||
<modules>
|
||||
<module>test-apps</module>
|
||||
<module>servers</module>
|
||||
<module>tests</module>
|
||||
<module>test-apps</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:xalan="http://xml.apache.org/xalan"
|
||||
version="2.0"
|
||||
exclude-result-prefixes="xalan">
|
||||
|
||||
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
|
||||
<xsl:strip-space elements="*"/>
|
||||
|
||||
<xsl:param name="worker.io-threads" select="'16'"/>
|
||||
<xsl:param name="worker.task-max-threads" select="'128'"/>
|
||||
|
||||
<!--set worker threads-->
|
||||
<xsl:template match="//*[local-name()='worker' and @name='default']">
|
||||
<worker name="default" io-threads="{$worker.io-threads}" task-max-threads="{$worker.task-max-threads}" />
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="@*|node()">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="@*|node()" />
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
|
@ -0,0 +1 @@
|
|||
admin=c22052286cd5d72239a90fe193737253
|
|
@ -124,6 +124,61 @@
|
|||
</transformationSets>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>io-worker-threads</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>transform</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformationSets>
|
||||
<transformationSet>
|
||||
<dir>${app.server.jboss.home}/standalone/configuration</dir>
|
||||
<includes>
|
||||
<include>standalone.xml</include>
|
||||
<include>standalone-ha.xml</include>
|
||||
</includes>
|
||||
<stylesheet>${common.resources}/io.xsl</stylesheet>
|
||||
<outputDir>${app.server.jboss.home}/standalone/configuration</outputDir>
|
||||
<parameters>
|
||||
<parameter>
|
||||
<name>worker.io-threads</name>
|
||||
<value>${app.server.worker.io-threads}</value>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>worker.task-max-threads</name>
|
||||
<value>${app.server.worker.task-max-threads}</value>
|
||||
</parameter>
|
||||
</parameters>
|
||||
</transformationSet>
|
||||
</transformationSets>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>enable-jboss-mgmt-admin</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${app.server.jboss.home}/standalone/configuration</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${common.resources}</directory>
|
||||
<includes>
|
||||
<include>mgmt-users.properties</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<overwrite>true</overwrite>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
|
||||
<properties>
|
||||
<app.server.saml.adapter.supported>false</app.server.saml.adapter.supported>
|
||||
<app.server.worker.io-threads>${jboss.default.worker.io-threads}</app.server.worker.io-threads>
|
||||
<app.server.worker.task-max-threads>${jboss.default.worker.task-max-threads}</app.server.worker.task-max-threads>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:xalan="http://xml.apache.org/xalan"
|
||||
version="2.0"
|
||||
exclude-result-prefixes="xalan">
|
||||
|
||||
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
|
||||
<xsl:strip-space elements="*"/>
|
||||
|
||||
<xsl:param name="worker.io-threads" select="'16'"/>
|
||||
<xsl:param name="worker.task-max-threads" select="'128'"/>
|
||||
|
||||
<!--set worker threads-->
|
||||
<xsl:template match="//*[local-name()='worker' and @name='default']">
|
||||
<worker name="default" io-threads="{$worker.io-threads}" task-max-threads="{$worker.task-max-threads}" />
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="@*|node()">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="@*|node()" />
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
|
@ -0,0 +1 @@
|
|||
admin=c22052286cd5d72239a90fe193737253
|
|
@ -0,0 +1,37 @@
|
|||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:xalan="http://xml.apache.org/xalan"
|
||||
version="2.0"
|
||||
exclude-result-prefixes="xalan">
|
||||
|
||||
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
|
||||
<xsl:strip-space elements="*"/>
|
||||
|
||||
<!--add socket binding-->
|
||||
<xsl:template match="//*[local-name()='socket-binding-group' and @name='standard-sockets']/*[local-name()='socket-binding' and @name='modcluster']">
|
||||
<socket-binding name="modcluster" interface="private" port="0" multicast-address="${{jboss.default.multicast.address:230.0.0.4}}" multicast-port="23364"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="@*|node()">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="@*|node()" />
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
|
@ -200,9 +200,62 @@
|
|||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>xml-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>io-worker-threads</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>transform</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformationSets>
|
||||
<transformationSet>
|
||||
<dir>${auth.server.home}/standalone/configuration</dir>
|
||||
<includes>
|
||||
<include>standalone.xml</include>
|
||||
<include>standalone-ha.xml</include>
|
||||
</includes>
|
||||
<stylesheet>${common.resources}/io.xsl</stylesheet>
|
||||
<outputDir>${auth.server.home}/standalone/configuration</outputDir>
|
||||
<parameters>
|
||||
<parameter>
|
||||
<name>worker.io-threads</name>
|
||||
<value>${auth.server.worker.io-threads}</value>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>worker.task-max-threads</name>
|
||||
<value>${auth.server.worker.task-max-threads}</value>
|
||||
</parameter>
|
||||
</parameters>
|
||||
</transformationSet>
|
||||
</transformationSets>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>enable-jboss-mgmt-admin</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${auth.server.home}/standalone/configuration</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${common.resources}</directory>
|
||||
<includes>
|
||||
<include>mgmt-users.properties</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<overwrite>true</overwrite>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
|
@ -577,6 +630,25 @@
|
|||
</transformationSets>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>mod-cluster-configuration</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>transform</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformationSets>
|
||||
<transformationSet>
|
||||
<dir>${auth.server.home}/standalone/configuration</dir>
|
||||
<includes>
|
||||
<include>standalone-ha.xml</include>
|
||||
</includes>
|
||||
<stylesheet>${common.resources}/mod_cluster.xsl</stylesheet>
|
||||
<outputDir>${auth.server.home}/standalone/configuration</outputDir>
|
||||
</transformationSet>
|
||||
</transformationSets>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
|
|
@ -29,6 +29,11 @@
|
|||
<packaging>pom</packaging>
|
||||
<name>Auth Server</name>
|
||||
|
||||
<properties>
|
||||
<auth.server.worker.io-threads>${jboss.default.worker.io-threads}</auth.server.worker.io-threads>
|
||||
<auth.server.worker.task-max-threads>${jboss.default.worker.task-max-threads}</auth.server.worker.task-max-threads>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
<module>services</module>
|
||||
<module>jboss</module>
|
||||
|
|
|
@ -47,6 +47,8 @@
|
|||
<!--<fuse62.version>6.2.0.redhat-133</fuse62.version>-->
|
||||
<fuse62.version>6.2.1.redhat-084</fuse62.version>
|
||||
|
||||
<jboss.default.worker.io-threads>16</jboss.default.worker.io-threads>
|
||||
<jboss.default.worker.task-max-threads>128</jboss.default.worker.task-max-threads>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
|
||||
<properties>
|
||||
<wildfly.balancer.home>${project.build.directory}/unpacked/wildfly-${wildfly.version}</wildfly.balancer.home>
|
||||
<wildfly.balancer.worker.io-threads>${jboss.default.worker.io-threads}</wildfly.balancer.worker.io-threads>
|
||||
<wildfly.balancer.worker.task-max-threads>${jboss.default.worker.task-max-threads}</wildfly.balancer.worker.task-max-threads>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
|
@ -70,6 +72,35 @@
|
|||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>xml-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>io-worker-threads</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>transform</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformationSets>
|
||||
<transformationSet>
|
||||
<dir>${wildfly.balancer.home}/standalone/configuration</dir>
|
||||
<includes>
|
||||
<include>standalone.xml</include>
|
||||
</includes>
|
||||
<stylesheet>src/main/xslt/io.xsl</stylesheet>
|
||||
<outputDir>${wildfly.balancer.home}/standalone/configuration</outputDir>
|
||||
<parameters>
|
||||
<parameter>
|
||||
<name>worker.io-threads</name>
|
||||
<value>${wildfly.balancer.worker.io-threads}</value>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>worker.task-max-threads</name>
|
||||
<value>${wildfly.balancer.worker.task-max-threads}</value>
|
||||
</parameter>
|
||||
</parameters>
|
||||
</transformationSet>
|
||||
</transformationSets>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>configure-mod-cluster</id>
|
||||
<phase>process-resources</phase>
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:xalan="http://xml.apache.org/xalan"
|
||||
version="2.0"
|
||||
exclude-result-prefixes="xalan">
|
||||
|
||||
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
|
||||
<xsl:strip-space elements="*"/>
|
||||
|
||||
<xsl:param name="worker.io-threads" select="'16'"/>
|
||||
<xsl:param name="worker.task-max-threads" select="'128'"/>
|
||||
|
||||
<!--set worker threads-->
|
||||
<xsl:template match="//*[local-name()='worker' and @name='default']">
|
||||
<worker name="default" io-threads="{$worker.io-threads}" task-max-threads="{$worker.task-max-threads}" />
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="@*|node()">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="@*|node()" />
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
|
@ -17,18 +17,14 @@
|
|||
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:xalan="http://xml.apache.org/xalan"
|
||||
xmlns:s="urn:jboss:domain:4.0"
|
||||
xmlns:u="urn:jboss:domain:undertow:3.0"
|
||||
version="2.0"
|
||||
exclude-result-prefixes="xalan j u">
|
||||
|
||||
<xsl:param name="config"/>
|
||||
exclude-result-prefixes="xalan">
|
||||
|
||||
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
|
||||
<xsl:strip-space elements="*"/>
|
||||
|
||||
<!--enable mod_cluster extension-->
|
||||
<xsl:template match="//s:extensions">
|
||||
<xsl:template match="//*[local-name()='extensions']">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="node()|@*"/>
|
||||
<extension module="org.jboss.as.modcluster"/>
|
||||
|
@ -36,7 +32,7 @@
|
|||
</xsl:template>
|
||||
|
||||
<!--add filter-ref-->
|
||||
<xsl:template match="//u:server[@name='default-server']/u:host[@name='default-host']">
|
||||
<xsl:template match="//*[local-name()='server' and @name='default-server']/*[local-name()='host' and @name='default-host']">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="node()|@*"/>
|
||||
<filter-ref name="modcluster"/>
|
||||
|
@ -44,23 +40,34 @@
|
|||
</xsl:template>
|
||||
|
||||
<!--add filter-->
|
||||
<xsl:template match="//u:filters">
|
||||
<xsl:template match="//*[local-name()='filters']">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="node()|@*"/>
|
||||
<mod-cluster
|
||||
name="modcluster"
|
||||
advertise-socket-binding="modcluster"
|
||||
advertise-frequency="${{modcluster.advertise-frequency:2000}}"
|
||||
management-socket-binding="http"
|
||||
enable-http2="true"
|
||||
/>
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<!--add socket binding-->
|
||||
<xsl:template match="//s:socket-binding-group[@name='standard-sockets']">
|
||||
|
||||
<!--add private interface -->
|
||||
<xsl:template match="/*[local-name()='server']/*[local-name()='interfaces']">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="node()|@*"/>
|
||||
<socket-binding name="modcluster" port="${{modcluster.port:23364}}" multicast-address="${{modcluster.multicast-address:224.0.1.105}}"/>
|
||||
<interface name="private">
|
||||
<inet-address value="${{jboss.bind.address.private:127.0.0.1}}"/>
|
||||
</interface>
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<!--add socket binding-->
|
||||
<xsl:template match="//*[local-name()='socket-binding-group' and @name='standard-sockets']">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="node()|@*"/>
|
||||
<socket-binding name="modcluster" interface="private" port="23364" multicast-address="${{jboss.default.multicast.address:230.0.0.4}}"/>
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
|
|
|
@ -51,13 +51,15 @@ public class AppServerTestEnricher {
|
|||
}
|
||||
|
||||
public static String getAppServerContextRoot(int clusterPortOffset) {
|
||||
String host = System.getProperty("app.server.host", "localhost");
|
||||
int httpPort = Integer.parseInt(System.getProperty("app.server.http.port")); // property must be set
|
||||
int httpsPort = Integer.parseInt(System.getProperty("app.server.https.port")); // property must be set
|
||||
boolean sslRequired = Boolean.parseBoolean(System.getProperty("app.server.ssl.required"));
|
||||
|
||||
return sslRequired
|
||||
? "https://localhost:" + (httpsPort + clusterPortOffset)
|
||||
: "http://localhost:" + (httpPort + clusterPortOffset);
|
||||
boolean sslRequired = Boolean.parseBoolean(System.getProperty("app.server.ssl.required"));
|
||||
String scheme = sslRequired ? "https" : "http";
|
||||
int port = sslRequired ? httpsPort : httpPort;
|
||||
|
||||
return String.format("%s://%s:%s", scheme, host, port + clusterPortOffset);
|
||||
}
|
||||
|
||||
public void updateTestContextWithAppServerInfo(@Observes(precedence = 1) BeforeClass event) {
|
||||
|
|
|
@ -81,13 +81,15 @@ public class AuthServerTestEnricher {
|
|||
}
|
||||
|
||||
public static String getAuthServerContextRoot(int clusterPortOffset) {
|
||||
String host = System.getProperty("auth.server.host", "localhost");
|
||||
int httpPort = Integer.parseInt(System.getProperty("auth.server.http.port")); // property must be set
|
||||
int httpsPort = Integer.parseInt(System.getProperty("auth.server.https.port")); // property must be set
|
||||
boolean sslRequired = Boolean.parseBoolean(System.getProperty("auth.server.ssl.required"));
|
||||
|
||||
return sslRequired
|
||||
? "https://localhost:" + (httpsPort + clusterPortOffset)
|
||||
: "http://localhost:" + (httpPort + clusterPortOffset);
|
||||
boolean sslRequired = Boolean.parseBoolean(System.getProperty("auth.server.ssl.required"));
|
||||
String scheme = sslRequired ? "https" : "http";
|
||||
int port = sslRequired ? httpsPort : httpPort;
|
||||
|
||||
return String.format("%s://%s:%s", scheme, host, port + clusterPortOffset);
|
||||
}
|
||||
|
||||
public void initializeSuiteContext(@Observes(precedence = 2) BeforeSuite event) {
|
||||
|
|
|
@ -135,6 +135,18 @@
|
|||
</configuration>
|
||||
</container>
|
||||
|
||||
<container qualifier="auth-server-remote" mode="suite" >
|
||||
<configuration>
|
||||
<property name="enabled">${auth.server.remote}</property>
|
||||
<property name="adapterImplClass">org.jboss.as.arquillian.container.remote.RemoteDeployableContainer</property>
|
||||
|
||||
<property name="managementAddress">${auth.server.host}</property>
|
||||
<property name="managementPort">${auth.server.management.port}</property>
|
||||
<property name="username">admin</property>
|
||||
<property name="password">admin</property>
|
||||
</configuration>
|
||||
</container>
|
||||
|
||||
<!-- PREVIOUS VERSIONS OF KEYCLOAK FOR MIGRATION TESTS -->
|
||||
|
||||
<container qualifier="auth-server-jboss-kc16" mode="manual" >
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
<properties>
|
||||
<common.resources>${project.parent.basedir}/common</common.resources>
|
||||
<adapter.libs.home>${app.server.home}/modules/system/add-ons/keycloak</adapter.libs.home>
|
||||
<app.server.type>managed</app.server.type>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
|
@ -85,7 +86,8 @@
|
|||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.wildfly</groupId>
|
||||
<artifactId>wildfly-arquillian-container-managed</artifactId>
|
||||
<artifactId>wildfly-arquillian-container-${app.server.type}</artifactId>
|
||||
<version>${arquillian-wildfly-container.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
|
@ -135,4 +137,4 @@
|
|||
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
#
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.keycloak.testsuite</groupId>
|
||||
<artifactId>integration-arquillian-tests-adapters-jboss</artifactId>
|
||||
<version>2.0.0.CR1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>integration-arquillian-tests-adapters-remote</artifactId>
|
||||
|
||||
<name>Adapter Tests - JBoss - Remote</name>
|
||||
|
||||
<properties>
|
||||
<app.server>remote</app.server>
|
||||
<app.server.type>remote</app.server.type>
|
||||
<app.server.skip.unpack>true</app.server.skip.unpack>
|
||||
<app.server.arquillian.xsl>src/test/resources/xslt/arquillian.xsl</app.server.arquillian.xsl>
|
||||
|
||||
<exclude.htmlunit>**/performance/htmlunit/**/*Test.java</exclude.htmlunit>
|
||||
<exclude.httpclient>**/performance/httpclient/**/*Test.java</exclude.httpclient>
|
||||
|
||||
<logging.loginlogout>INFO</logging.loginlogout>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-app-profile-jee-quickstart</id>
|
||||
<phase>generate-test-resources</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.keycloak.quickstart</groupId>
|
||||
<artifactId>keycloak-quickstart-app-profile-jee</artifactId>
|
||||
<version>0.5-SNAPSHOT</version>
|
||||
<type>war</type>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<outputDirectory>${examples.home}</outputDirectory>
|
||||
<overWriteIfNewer>true</overWriteIfNewer>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>enforce</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<rules>
|
||||
<requireActiveProfile>
|
||||
<profiles>auth-server-remote</profiles>
|
||||
<!--
|
||||
NOTE: Running adapter test against remote app-server
|
||||
also requires remote mode for the auth-server.
|
||||
|
||||
There are problems when `wildfly-arquillian-container-remote` is on classpath
|
||||
together with `wildfly-arquillian-container-managed`.
|
||||
-->
|
||||
</requireActiveProfile>
|
||||
</rules>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>${exclude.htmlunit}</exclude>
|
||||
<exclude>${exclude.httpclient}</exclude>
|
||||
</excludes>
|
||||
<systemPropertyVariables>
|
||||
<logging.loginlogout>${logging.loginlogout}</logging.loginlogout>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>auth-server-remote</id>
|
||||
<!--dummy profile for enforcer-->
|
||||
</profile>
|
||||
<profile>
|
||||
<id>no-offset</id>
|
||||
<properties>
|
||||
<app.server.port.offset>0</app.server.port.offset>
|
||||
<app.server.http.port>8080</app.server.http.port>
|
||||
<app.server.https.port>8443</app.server.https.port>
|
||||
<app.server.management.port>9990</app.server.management.port>
|
||||
<app.server.management.port.jmx>9999</app.server.management.port.jmx>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>htmlunit</id>
|
||||
<properties>
|
||||
<exclude.htmlunit>-</exclude.htmlunit>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>httpclient</id>
|
||||
<properties>
|
||||
<exclude.httpclient>-</exclude.httpclient>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>debug</id>
|
||||
<properties>
|
||||
<logging.loginlogout>TRACE</logging.loginlogout>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<warmup.load>1</warmup.load>
|
||||
<warmup.duration>10</warmup.duration>
|
||||
<max.iterations>0</max.iterations>
|
||||
<sleep.between.repeats>30</sleep.between.repeats>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,28 @@
|
|||
package org.keycloak.testsuite.performance.page;
|
||||
|
||||
import java.net.URL;
|
||||
import org.jboss.arquillian.container.test.api.OperateOnDeployment;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public class AppProfileJEE extends AbstractPageWithInjectedUrl {
|
||||
|
||||
public static final String DEPLOYMENT_NAME = "app-profile-jee";
|
||||
|
||||
@ArquillianResource
|
||||
@OperateOnDeployment(DEPLOYMENT_NAME)
|
||||
private URL url;
|
||||
|
||||
@Override
|
||||
public URL getInjectedUrl() {
|
||||
//EAP6 URL fix
|
||||
URL fixedUrl = createInjectedURL(DEPLOYMENT_NAME);
|
||||
return fixedUrl != null ? fixedUrl : url;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package org.keycloak.testsuite.performance;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public class LoginLogoutParameters {
|
||||
|
||||
public static final Integer AVERAGE_LOGIN_TIME_LIMIT = Integer.parseInt(System.getProperty("average.login.time.limit", "500"));
|
||||
public static final Integer AVERAGE_LOGOUT_TIME_LIMIT = Integer.parseInt(System.getProperty("average.logout.time.limit", "500"));
|
||||
|
||||
public static final String ACCESS_REQUEST_TIME = "ACCESS_REQUEST";
|
||||
public static final String LOGIN_REQUEST_TIME = "LOGIN_REQUEST";
|
||||
public static final String LOGIN_VERIFY_REQUEST_TIME = "LOGIN_VERIFY_REQUEST";
|
||||
public static final String LOGOUT_REQUEST_TIME = "LOGOUT_REQUEST";
|
||||
public static final String LOGOUT_VERIFY_REQUEST_TIME = "LOGOUT_VERIFY_REQUEST";
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package org.keycloak.testsuite.performance;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public class OperationTimeoutException extends Exception {
|
||||
|
||||
private final String metric;
|
||||
private final long value;
|
||||
|
||||
public OperationTimeoutException(String metric, Throwable cause) {
|
||||
this(metric, 0, cause);
|
||||
}
|
||||
|
||||
public OperationTimeoutException(String metric, long value, Throwable cause) {
|
||||
super(cause);
|
||||
this.metric = metric;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getMetric() {
|
||||
return metric;
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
package org.keycloak.testsuite.performance;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
|
||||
import org.keycloak.testsuite.performance.metrics.impl.Results;
|
||||
import org.keycloak.testsuite.performance.metrics.impl.ResultsWithThroughput;
|
||||
import static org.keycloak.testsuite.util.WaitUtils.pause;
|
||||
|
||||
/**
|
||||
* PerformanceTest.
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public abstract class PerformanceTest extends AbstractExampleAdapterTest {
|
||||
|
||||
private final Logger LOG = Logger.getLogger(PerformanceTest.class);
|
||||
|
||||
public static final Integer WARMUP_LOAD = Integer.parseInt(System.getProperty("warmup.load", "5"));
|
||||
public static final Integer WARMUP_DURATION = Integer.parseInt(System.getProperty("warmup.duration", "30"));
|
||||
|
||||
public static final Integer INITIAL_LOAD = Integer.parseInt(System.getProperty("initial.load", "10")); // load for the first iteration
|
||||
public static final Integer LOAD_INCREASE = Integer.parseInt(System.getProperty("load.increase", "10")); // how many threads to add before each iteration
|
||||
public static final Integer LOAD_INCREASE_RATE = Integer.parseInt(System.getProperty("load.increase.rate", "2")); // how fast to add the new threads per second
|
||||
|
||||
public static final Integer MEASUREMENT_DURATION = Integer.parseInt(System.getProperty("measurement.duration", "20")); // duration of one measurement iteration
|
||||
|
||||
public static final Integer MAX_ITERATIONS = Integer.parseInt(System.getProperty("max.iterations", "10"));
|
||||
public static final Integer MAX_THREADS = Integer.parseInt(System.getProperty("max.threads", "1000"));
|
||||
|
||||
public static final Integer SLEEP_BETWEEN_REPEATS = Integer.parseInt(System.getProperty("sleep.between.repeats", "0"));
|
||||
|
||||
private final double AVERAGE_TIMEOUT_PERCENTAGE_LIMIT = Double.parseDouble(System.getProperty("average.timeout.percentage.limit", "0.01"));
|
||||
|
||||
private int currentLoad;
|
||||
|
||||
private ExecutorService executorService;
|
||||
|
||||
protected PerformanceTestMetrics metrics = new PerformanceTestMetrics();
|
||||
protected PerformanceTestMetrics timeouts = new PerformanceTestMetrics();
|
||||
|
||||
protected List<ResultsWithThroughput> resultsList = new ArrayList<>();
|
||||
protected List<ResultsWithThroughput> timeoutResultsList = new ArrayList<>();
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
if (WARMUP_LOAD > INITIAL_LOAD) {
|
||||
throw new IllegalArgumentException("'warmup.load' cannot be larger than 'initial.load'");
|
||||
}
|
||||
|
||||
executorService = Executors.newFixedThreadPool(MAX_THREADS);
|
||||
currentLoad = 0;
|
||||
|
||||
metrics.clear();
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() throws IOException, InterruptedException {
|
||||
executorService.shutdown();
|
||||
LOG.info("Waiting for threadpool termination.");
|
||||
executorService.awaitTermination(10, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
increaseLoadBy(WARMUP_LOAD); // increase to warmup load
|
||||
warmup();
|
||||
|
||||
for (int i = 0; i < MAX_ITERATIONS; i++) {
|
||||
|
||||
int loadIncrease = (i == 0)
|
||||
? INITIAL_LOAD - WARMUP_LOAD // increase from warmup to initial load
|
||||
: LOAD_INCREASE; // increase load between iterations
|
||||
|
||||
increaseLoadBy(loadIncrease);
|
||||
measurePerformance();
|
||||
|
||||
if (!isThereEnoughThreadsForNextIteration(LOAD_INCREASE)) {
|
||||
LOG.warn("Threadpool capacity reached. Stopping the test.");
|
||||
break;
|
||||
}
|
||||
if (!isLatestResultsWithinLimits()) {
|
||||
LOG.warn("The latest measurement surpassed expected limit. Stopping the test.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void warmup() {
|
||||
LOG.info("Warming up for " + WARMUP_DURATION + " s");
|
||||
pauseWithErrorChecking(WARMUP_DURATION * 1000);
|
||||
}
|
||||
|
||||
private boolean isThereEnoughThreadsForNextIteration(int loadIncrease) {
|
||||
return currentLoad + loadIncrease <= MAX_THREADS;
|
||||
}
|
||||
|
||||
private void increaseLoadBy(int loadIncrease) {
|
||||
if (loadIncrease < 0) {
|
||||
throw new IllegalArgumentException("Cannot increase load by a negative number (" + loadIncrease + ").");
|
||||
}
|
||||
if (!isThereEnoughThreadsForNextIteration(loadIncrease)) {
|
||||
throw new IllegalArgumentException("Cannot increase load beyond threadpool capacity (" + MAX_THREADS + ").");
|
||||
}
|
||||
if (loadIncrease > 0) {
|
||||
LOG.info(String.format("Increasing load from %s to %s.", currentLoad, currentLoad + loadIncrease));
|
||||
for (int t = 0; t < loadIncrease; t++) {
|
||||
executorService.submit(newRunnable());
|
||||
currentLoad++;
|
||||
pauseWithErrorChecking(1000 / LOAD_INCREASE_RATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void measurePerformance() {
|
||||
LOG.info("Measuring performance");
|
||||
LOG.info("Iteration: " + (resultsList.size() + 1));
|
||||
LOG.info("Duration: " + MEASUREMENT_DURATION + " s");
|
||||
LOG.info("Load: " + currentLoad);
|
||||
|
||||
metrics.reset();
|
||||
pauseWithErrorChecking(MEASUREMENT_DURATION * 1000);
|
||||
resultsList.add(metrics.computeMetrics());
|
||||
timeoutResultsList.add(timeouts.computeMetrics());
|
||||
|
||||
getLatestResults().logResults(); // to file
|
||||
LOG.info("Timeouts: " + getLatestTimeoutResults());
|
||||
}
|
||||
|
||||
protected ResultsWithThroughput getLatestResults() {
|
||||
return resultsList.isEmpty() ? null : resultsList.get(resultsList.size() - 1);
|
||||
}
|
||||
|
||||
protected Results getLatestTimeoutResults() {
|
||||
return timeoutResultsList.isEmpty() ? null : timeoutResultsList.get(timeoutResultsList.size() - 1);
|
||||
}
|
||||
|
||||
private Throwable error = null;
|
||||
|
||||
public synchronized Throwable getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public synchronized void setError(Throwable error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
protected void pauseWithErrorChecking(long millis) {
|
||||
pauseWithErrorChecking(millis, 1000);
|
||||
}
|
||||
|
||||
protected void pauseWithErrorChecking(long millis, long checkIntervals) {
|
||||
long count = millis / checkIntervals;
|
||||
long remainder = millis % checkIntervals;
|
||||
for (int i = 0; i < count + 1; i++) { // repeat 'count' times + once for remainder
|
||||
if (i < count || remainder > 0) { // on last iteration check if any remainder
|
||||
pause(checkIntervals);
|
||||
if (getError() != null) {
|
||||
throw new RuntimeException("PerformanceTestRunnable threw an exception. Stopping the test.", getError());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean isLatestResultsWithinLimits();
|
||||
|
||||
protected boolean isLatestTimeoutsWithinLimits() {
|
||||
boolean socketTimeoutsWithinLimits = true;
|
||||
for (String metric : getLatestResults().keySet()) {
|
||||
long timemoutCount = getLatestTimeoutResults().containsKey(metric) ? getLatestTimeoutResults().get(metric).getCount() : 0;
|
||||
double timeoutPercentage = (double) timemoutCount / getLatestResults().get(metric).getCount();
|
||||
socketTimeoutsWithinLimits = socketTimeoutsWithinLimits && timeoutPercentage < AVERAGE_TIMEOUT_PERCENTAGE_LIMIT;
|
||||
}
|
||||
return socketTimeoutsWithinLimits;
|
||||
}
|
||||
|
||||
protected abstract Runnable newRunnable();
|
||||
|
||||
public abstract class Runnable extends RepeatRunnable {
|
||||
|
||||
protected final Timer timer;
|
||||
|
||||
public Runnable() {
|
||||
super(SLEEP_BETWEEN_REPEATS * 1000);
|
||||
this.timer = new Timer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void repeat() {
|
||||
try {
|
||||
performanceScenario();
|
||||
} catch (OperationTimeoutException ex) {
|
||||
timeouts.addValue(ex.getMetric(), ex.getValue());
|
||||
LOG.debug(String.format("Operatin %s timed out. Cause: %s.", ex.getMetric(), ex.getCause()));
|
||||
} catch (AssertionError | Exception ex) {
|
||||
setError(ex);
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void performanceScenario() throws Exception;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package org.keycloak.testsuite.performance;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.keycloak.testsuite.performance.metrics.ComputedMetric;
|
||||
import org.keycloak.testsuite.performance.metrics.ComputedMetrics;
|
||||
import org.keycloak.testsuite.performance.metrics.impl.ArrayListMetric;
|
||||
import org.keycloak.testsuite.performance.metrics.impl.MovingAverageMetric;
|
||||
import org.keycloak.testsuite.performance.metrics.impl.ResultsWithThroughput;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public class PerformanceTestMetrics extends ConcurrentHashMap<String,ComputedMetric> implements ComputedMetrics {
|
||||
|
||||
public static final String METRIC_MOVING_AVERAGE = "MovingAverage";
|
||||
public static final String METRIC_ARRAY_LIST = "ArrayList";
|
||||
public static final String METRIC = System.getProperty("metric", METRIC_MOVING_AVERAGE);
|
||||
|
||||
Timer timer = new Timer();
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
clear();
|
||||
timer.reset();
|
||||
}
|
||||
|
||||
private ComputedMetric getOrCreate(String metric) {
|
||||
ComputedMetric m = get(metric);
|
||||
if (m == null) {
|
||||
if (METRIC_ARRAY_LIST.equals(metric)) {
|
||||
m = new ArrayListMetric();
|
||||
} else {
|
||||
m = new MovingAverageMetric();
|
||||
}
|
||||
put(metric, m);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addValue(String metric, long value) {
|
||||
getOrCreate(metric).addValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultsWithThroughput computeMetrics() {
|
||||
return new ResultsWithThroughput(this, timer.getElapsedTime());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package org.keycloak.testsuite.performance;
|
||||
|
||||
import static org.keycloak.testsuite.util.WaitUtils.pause;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public abstract class RepeatRunnable implements Runnable {
|
||||
|
||||
private long sleepBetweenRepeatsMillis;
|
||||
private long repeatCounter;
|
||||
|
||||
public RepeatRunnable() {
|
||||
this(0);
|
||||
this.repeatCounter = 0;
|
||||
}
|
||||
|
||||
public RepeatRunnable(long sleepBetweenRepeatsMillis) {
|
||||
this.sleepBetweenRepeatsMillis = sleepBetweenRepeatsMillis;
|
||||
}
|
||||
|
||||
public void setSleepBetweenRepeatsMillis(long sleepBetweenRepeatsMillis) {
|
||||
this.sleepBetweenRepeatsMillis = sleepBetweenRepeatsMillis;
|
||||
}
|
||||
|
||||
public long getRepeatCounter() {
|
||||
return repeatCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
repeat();
|
||||
repeatCounter++;
|
||||
pause(sleepBetweenRepeatsMillis);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void repeat();
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package org.keycloak.testsuite.performance;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public final class Timer {
|
||||
|
||||
private long time;
|
||||
|
||||
public Timer() {
|
||||
reset();
|
||||
}
|
||||
|
||||
public long reset() {
|
||||
time = new Date().getTime();
|
||||
return time;
|
||||
}
|
||||
|
||||
public long getElapsedTime() {
|
||||
return new Date().getTime() - time;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
package org.keycloak.testsuite.performance.htmlunit;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.shrinkwrap.api.ShrinkWrap;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.Before;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
import org.keycloak.testsuite.performance.page.AppProfileJEE;
|
||||
import org.openqa.selenium.By;
|
||||
import static org.keycloak.testsuite.performance.LoginLogoutParameters.AVERAGE_LOGIN_TIME_LIMIT;
|
||||
import static org.keycloak.testsuite.performance.LoginLogoutParameters.AVERAGE_LOGOUT_TIME_LIMIT;
|
||||
import static org.keycloak.testsuite.performance.LoginLogoutParameters.LOGIN_REQUEST_TIME;
|
||||
import static org.keycloak.testsuite.performance.LoginLogoutParameters.LOGOUT_REQUEST_TIME;
|
||||
import static org.keycloak.testsuite.performance.LoginLogoutParameters.ACCESS_REQUEST_TIME;
|
||||
import static org.keycloak.testsuite.performance.LoginLogoutParameters.LOGIN_VERIFY_REQUEST_TIME;
|
||||
import static org.keycloak.testsuite.performance.LoginLogoutParameters.LOGOUT_VERIFY_REQUEST_TIME;
|
||||
import org.keycloak.testsuite.performance.PerformanceTest;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.keycloak.testsuite.performance.OperationTimeoutException;
|
||||
import static org.keycloak.testsuite.util.IOUtil.loadRealm;
|
||||
import org.openqa.selenium.TimeoutException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
@AppServerContainer("app-server-remote")
|
||||
public class HtmlUnitLoginLogoutPerfTest extends HtmlUnitPerformanceTest {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(HtmlUnitLoginLogoutPerfTest.class);
|
||||
|
||||
private static final String EXAMPLES = "Examples";
|
||||
|
||||
private String unsecuredUrl;
|
||||
private String securedUrl;
|
||||
private String username;
|
||||
private String password;
|
||||
private String loginPageUrl;
|
||||
|
||||
@Page
|
||||
protected AppProfileJEE appProfileJEEPage;
|
||||
|
||||
protected static WebArchive warDeployment(String filename) throws IOException {
|
||||
return ShrinkWrap.createFromZipFile(WebArchive.class,
|
||||
new File(EXAMPLES_HOME + "/" + filename + ".war"))
|
||||
.addAsWebInfResource(jbossDeploymentStructure, JBOSS_DEPLOYMENT_STRUCTURE_XML);
|
||||
}
|
||||
|
||||
@Deployment(name = AppProfileJEE.DEPLOYMENT_NAME)
|
||||
private static WebArchive appProfileJEE() throws IOException {
|
||||
return warDeployment("keycloak-quickstart-app-profile-jee-0.5-SNAPSHOT");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultPageUriParameters() {
|
||||
super.setDefaultPageUriParameters();
|
||||
testRealmPage.setAuthRealm(EXAMPLES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
|
||||
testRealms.add(loadRealm("/examples-realm.json"));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void beforeLoginLogoutTest() {
|
||||
unsecuredUrl = appProfileJEEPage + "/index.jsp";
|
||||
securedUrl = appProfileJEEPage + "/profile.jsp";
|
||||
username = "secure-user";
|
||||
password = "password";
|
||||
loginPageUrl = testRealmLoginPage.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PerformanceTest.Runnable newRunnable() {
|
||||
return new Runnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isLatestResultsWithinLimits() {
|
||||
return getLatestResults().get(LOGIN_REQUEST_TIME).getAverage() < AVERAGE_LOGIN_TIME_LIMIT
|
||||
&& getLatestResults().get(LOGOUT_REQUEST_TIME).getAverage() < AVERAGE_LOGOUT_TIME_LIMIT;
|
||||
}
|
||||
|
||||
public class Runnable extends HtmlUnitPerformanceTest.Runnable {
|
||||
|
||||
@Override
|
||||
public void performanceScenario() throws Exception {
|
||||
LOG.debug(String.format("Starting login-logout scenario #%s", getRepeatCounter()));
|
||||
driver.manage().deleteAllCookies();
|
||||
|
||||
// ACCESS
|
||||
LOG.trace(String.format("Accessing secured URL: %s", securedUrl));
|
||||
try {
|
||||
timer.reset();
|
||||
driver.get(securedUrl);
|
||||
assertTrue(driver.getCurrentUrl().startsWith(loginPageUrl));
|
||||
} catch (TimeoutException ex) {
|
||||
throw new OperationTimeoutException(ACCESS_REQUEST_TIME, ex);
|
||||
}
|
||||
metrics.addValue(ACCESS_REQUEST_TIME, timer.getElapsedTime());
|
||||
|
||||
// LOGIN
|
||||
LOG.trace("Logging in");
|
||||
try {
|
||||
driver.findElement(By.id("username")).clear();
|
||||
driver.findElement(By.id("username")).sendKeys(username);
|
||||
driver.findElement(By.id("password")).clear();
|
||||
driver.findElement(By.id("password")).sendKeys(password);
|
||||
timer.reset();
|
||||
driver.findElement(By.name("login")).click();
|
||||
assertTrue(driver.getCurrentUrl().equals(securedUrl));
|
||||
} catch (TimeoutException ex) {
|
||||
throw new OperationTimeoutException(LOGIN_REQUEST_TIME, ex);
|
||||
}
|
||||
metrics.addValue(LOGIN_REQUEST_TIME, timer.getElapsedTime());
|
||||
|
||||
// VERIFY LOGIN
|
||||
LOG.trace("Verifying login");
|
||||
try {
|
||||
timer.reset();
|
||||
driver.get(securedUrl);
|
||||
assertTrue(driver.getCurrentUrl().equals(securedUrl));
|
||||
} catch (TimeoutException ex) {
|
||||
throw new OperationTimeoutException(LOGIN_VERIFY_REQUEST_TIME, ex);
|
||||
}
|
||||
metrics.addValue(LOGIN_VERIFY_REQUEST_TIME, timer.getElapsedTime());
|
||||
|
||||
// LOGOUT
|
||||
LOG.trace("Logging out");
|
||||
try {
|
||||
timer.reset();
|
||||
driver.findElement(By.xpath("//button[text()='Logout']")).click();
|
||||
assertTrue(driver.getCurrentUrl().startsWith(unsecuredUrl));
|
||||
} catch (TimeoutException ex) {
|
||||
throw new OperationTimeoutException(LOGOUT_REQUEST_TIME, ex);
|
||||
}
|
||||
metrics.addValue(LOGOUT_REQUEST_TIME, timer.getElapsedTime());
|
||||
|
||||
// VERIFY LOGOUT
|
||||
LOG.trace("Verifying logout");
|
||||
try {
|
||||
timer.reset();
|
||||
driver.get(securedUrl);
|
||||
assertTrue(driver.getCurrentUrl().startsWith(loginPageUrl));
|
||||
} catch (TimeoutException ex) {
|
||||
throw new OperationTimeoutException(LOGOUT_VERIFY_REQUEST_TIME, ex);
|
||||
}
|
||||
metrics.addValue(LOGOUT_VERIFY_REQUEST_TIME, timer.getElapsedTime());
|
||||
|
||||
LOG.trace("Logged out");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package org.keycloak.testsuite.performance.htmlunit;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.WebClient;
|
||||
import org.keycloak.testsuite.performance.PerformanceTest;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public abstract class HtmlUnitPerformanceTest extends PerformanceTest {
|
||||
|
||||
public abstract class Runnable extends PerformanceTest.Runnable {
|
||||
|
||||
protected HtmlUnitDriver driver;
|
||||
|
||||
public Runnable() {
|
||||
driver = new HtmlUnitDriver(true, false);
|
||||
}
|
||||
|
||||
public final class HtmlUnitDriver extends org.openqa.selenium.htmlunit.HtmlUnitDriver {
|
||||
|
||||
public HtmlUnitDriver(boolean javaScriptEnabled, boolean cssEnabled) {
|
||||
getWebClient().getOptions().setJavaScriptEnabled(javaScriptEnabled);
|
||||
getWebClient().getOptions().setCssEnabled(cssEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClient getWebClient() {
|
||||
return super.getWebClient();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
package org.keycloak.testsuite.performance.httpclient;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import static java.net.HttpURLConnection.HTTP_OK;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.shrinkwrap.api.ShrinkWrap;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.Before;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
import org.keycloak.testsuite.performance.page.AppProfileJEE;
|
||||
import static org.keycloak.testsuite.performance.LoginLogoutParameters.AVERAGE_LOGIN_TIME_LIMIT;
|
||||
import static org.keycloak.testsuite.performance.LoginLogoutParameters.AVERAGE_LOGOUT_TIME_LIMIT;
|
||||
import static org.keycloak.testsuite.performance.LoginLogoutParameters.LOGIN_REQUEST_TIME;
|
||||
import static org.keycloak.testsuite.performance.LoginLogoutParameters.LOGOUT_REQUEST_TIME;
|
||||
import static org.keycloak.testsuite.performance.LoginLogoutParameters.ACCESS_REQUEST_TIME;
|
||||
import static org.keycloak.testsuite.performance.LoginLogoutParameters.LOGIN_VERIFY_REQUEST_TIME;
|
||||
import static org.keycloak.testsuite.performance.LoginLogoutParameters.LOGOUT_VERIFY_REQUEST_TIME;
|
||||
import org.keycloak.testsuite.performance.PerformanceTest;
|
||||
import org.keycloak.testsuite.performance.OperationTimeoutException;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.keycloak.testsuite.util.IOUtil.loadRealm;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
@AppServerContainer("app-server-remote")
|
||||
public class HttpClientLoginLogoutPerfTest extends HttpClientPerformanceTest {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(HttpClientLoginLogoutPerfTest.class);
|
||||
|
||||
private static final String EXAMPLES = "Examples";
|
||||
|
||||
private String securedUrl;
|
||||
private String logoutUrl;
|
||||
private String username;
|
||||
private String password;
|
||||
private String loginPageUrl;
|
||||
|
||||
@Page
|
||||
protected AppProfileJEE appProfileJEEPage;
|
||||
|
||||
protected static WebArchive warDeployment(String filename) throws IOException {
|
||||
return ShrinkWrap.createFromZipFile(WebArchive.class,
|
||||
new File(EXAMPLES_HOME + "/" + filename + ".war"))
|
||||
.addAsWebInfResource(jbossDeploymentStructure, JBOSS_DEPLOYMENT_STRUCTURE_XML);
|
||||
}
|
||||
|
||||
@Deployment(name = AppProfileJEE.DEPLOYMENT_NAME)
|
||||
private static WebArchive appProfileJEE() throws IOException {
|
||||
return warDeployment("keycloak-quickstart-app-profile-jee-0.5-SNAPSHOT");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultPageUriParameters() {
|
||||
super.setDefaultPageUriParameters();
|
||||
testRealmPage.setAuthRealm(EXAMPLES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
|
||||
testRealms.add(loadRealm("/examples-realm.json"));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void beforeLoginLogoutTest() {
|
||||
securedUrl = appProfileJEEPage + "/profile.jsp";
|
||||
logoutUrl = appProfileJEEPage + "/index.jsp?logout=true";
|
||||
username = "secure-user";
|
||||
password = "password";
|
||||
loginPageUrl = testRealmLoginPage.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PerformanceTest.Runnable newRunnable() {
|
||||
return new Runnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isLatestResultsWithinLimits() {
|
||||
return isLatestTimeoutsWithinLimits()
|
||||
&& getLatestResults().get(LOGIN_REQUEST_TIME).getAverage() < AVERAGE_LOGIN_TIME_LIMIT
|
||||
&& getLatestResults().get(LOGOUT_REQUEST_TIME).getAverage() < AVERAGE_LOGOUT_TIME_LIMIT;
|
||||
}
|
||||
|
||||
public class Runnable extends HttpClientPerformanceTest.Runnable {
|
||||
|
||||
@Override
|
||||
public void performanceScenario() throws IOException, OperationTimeoutException {
|
||||
LOG.debug(String.format("Starting login-logout scenario #%s", getRepeatCounter()));
|
||||
context.getCookieStore().clear();
|
||||
|
||||
// ACCESS
|
||||
String pageContent;
|
||||
final HttpGet getSecuredPageRequest = new HttpGet(securedUrl);
|
||||
LOG.trace(String.format("Accessing secured URL: %s", getSecuredPageRequest));
|
||||
LOG.trace(getSecuredPageRequest);
|
||||
timer.reset();
|
||||
try (CloseableHttpResponse r = client.execute(getSecuredPageRequest, context)) {
|
||||
assertEquals(HTTP_OK, r.getStatusLine().getStatusCode());
|
||||
logRedirects();
|
||||
assertEquals(1, context.getRedirectLocations().size());
|
||||
assertTrue(getLastRedirect().toASCIIString().startsWith(loginPageUrl));
|
||||
pageContent = EntityUtils.toString(r.getEntity());
|
||||
} catch (SocketException ex) {
|
||||
throw new OperationTimeoutException(ACCESS_REQUEST_TIME, ex);
|
||||
} catch (SocketTimeoutException ex) {
|
||||
throw new OperationTimeoutException(ACCESS_REQUEST_TIME, ex.bytesTransferred, ex);
|
||||
}
|
||||
metrics.addValue(ACCESS_REQUEST_TIME, timer.getElapsedTime());
|
||||
|
||||
// LOGIN
|
||||
final HttpPost loginRequest = new HttpPost(getLoginUrlFromPage(pageContent));
|
||||
List<NameValuePair> credentials = new ArrayList<>();
|
||||
credentials.add(new BasicNameValuePair("username", username));
|
||||
credentials.add(new BasicNameValuePair("password", password));
|
||||
loginRequest.setEntity(new UrlEncodedFormEntity(credentials));
|
||||
|
||||
LOG.trace("Logging in");
|
||||
LOG.trace(loginRequest);
|
||||
timer.reset();
|
||||
try (CloseableHttpResponse r = client.execute(loginRequest, context)) {
|
||||
assertEquals(HTTP_OK, r.getStatusLine().getStatusCode());
|
||||
logRedirects();
|
||||
assertEquals(2, context.getRedirectLocations().size());
|
||||
assertTrue(getLastRedirect().toASCIIString().equals(securedUrl));
|
||||
} catch (SocketException ex) {
|
||||
throw new OperationTimeoutException(LOGIN_REQUEST_TIME, ex);
|
||||
} catch (SocketTimeoutException ex) {
|
||||
throw new OperationTimeoutException(LOGIN_REQUEST_TIME, ex.bytesTransferred, ex);
|
||||
}
|
||||
metrics.addValue(LOGIN_REQUEST_TIME, timer.getElapsedTime());
|
||||
|
||||
// VERIFY LOGIN
|
||||
LOG.trace("Verifying login");
|
||||
LOG.trace(getSecuredPageRequest);
|
||||
timer.reset();
|
||||
try (CloseableHttpResponse r = client.execute(getSecuredPageRequest, context)) {
|
||||
assertEquals(HTTP_OK, r.getStatusLine().getStatusCode());
|
||||
logRedirects();
|
||||
assertEquals(0, context.getRedirectLocations().size());
|
||||
} catch (SocketException ex) {
|
||||
throw new OperationTimeoutException(LOGIN_VERIFY_REQUEST_TIME, ex);
|
||||
} catch (SocketTimeoutException ex) {
|
||||
throw new OperationTimeoutException(LOGIN_VERIFY_REQUEST_TIME, ex.bytesTransferred, ex);
|
||||
}
|
||||
metrics.addValue(LOGIN_VERIFY_REQUEST_TIME, timer.getElapsedTime());
|
||||
|
||||
// LOGOUT
|
||||
final HttpGet logoutRequest = new HttpGet(logoutUrl);
|
||||
LOG.trace("Logging out");
|
||||
LOG.trace(logoutRequest);
|
||||
timer.reset();
|
||||
try (CloseableHttpResponse r = client.execute(logoutRequest, context)) {
|
||||
assertEquals(HTTP_OK, r.getStatusLine().getStatusCode());
|
||||
logRedirects();
|
||||
assertEquals(0, context.getRedirectLocations().size());
|
||||
} catch (SocketException ex) {
|
||||
throw new OperationTimeoutException(LOGOUT_REQUEST_TIME, ex);
|
||||
} catch (SocketTimeoutException ex) {
|
||||
throw new OperationTimeoutException(LOGOUT_REQUEST_TIME, ex.bytesTransferred, ex);
|
||||
}
|
||||
metrics.addValue(LOGOUT_REQUEST_TIME, timer.getElapsedTime());
|
||||
|
||||
// VERIFY LOGOUT
|
||||
LOG.trace("Verifying logout");
|
||||
LOG.trace(getSecuredPageRequest);
|
||||
timer.reset();
|
||||
try (CloseableHttpResponse r = client.execute(getSecuredPageRequest, context)) {
|
||||
assertEquals(HTTP_OK, r.getStatusLine().getStatusCode());
|
||||
logRedirects();
|
||||
assertEquals(1, context.getRedirectLocations().size());
|
||||
assertTrue(getLastRedirect().toASCIIString().startsWith(loginPageUrl));
|
||||
} catch (SocketException ex) {
|
||||
throw new OperationTimeoutException(LOGOUT_VERIFY_REQUEST_TIME, ex);
|
||||
} catch (SocketTimeoutException ex) {
|
||||
throw new OperationTimeoutException(LOGOUT_VERIFY_REQUEST_TIME, ex.bytesTransferred, ex);
|
||||
}
|
||||
metrics.addValue(LOGOUT_VERIFY_REQUEST_TIME, timer.getElapsedTime());
|
||||
|
||||
LOG.trace("Logged out");
|
||||
|
||||
}
|
||||
|
||||
private URI getLastRedirect() {
|
||||
return context.getRedirectLocations().get(context.getRedirectLocations().size() - 1);
|
||||
}
|
||||
|
||||
private void logRedirects() {
|
||||
int i = 0;
|
||||
for (URI uri : context.getRedirectLocations()) {
|
||||
LOG.trace(String.format("--> REDIRECT %s: %s", ++i, uri.toASCIIString()));
|
||||
}
|
||||
}
|
||||
|
||||
public String getRedirectLocation(CloseableHttpResponse r) {
|
||||
Header locationHeader = r.getFirstHeader("Location");
|
||||
assertNotNull(locationHeader);
|
||||
return locationHeader.getValue();
|
||||
}
|
||||
|
||||
private String getLoginUrlFromPage(String content) {
|
||||
String formActionRegex = "<form[^>]+action\\s*=\\s*['\"]([^'\"]+)['\"][^>]*>";
|
||||
Pattern p = Pattern.compile(formActionRegex);
|
||||
Matcher m = p.matcher(content);
|
||||
if (m.find()) {
|
||||
return m.group(1);
|
||||
} else {
|
||||
throw new IllegalStateException("Login url counldn't be parsed form page.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package org.keycloak.testsuite.performance.httpclient;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.apache.http.client.config.CookieSpecs;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.HttpDelete;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpHead;
|
||||
import org.apache.http.client.methods.HttpOptions;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.protocol.HttpClientContext;
|
||||
import org.apache.http.config.SocketConfig;
|
||||
import org.apache.http.impl.client.BasicCookieStore;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.DefaultRedirectStrategy;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.keycloak.testsuite.performance.PerformanceTest;
|
||||
import static org.keycloak.testsuite.performance.PerformanceTest.MAX_THREADS;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public abstract class HttpClientPerformanceTest extends PerformanceTest {
|
||||
|
||||
protected CloseableHttpClient client;
|
||||
|
||||
public static final Integer HTTP_CLIENT_SOCKET_TIMEOUT = Integer.parseInt(System.getProperty("httpclient.socket.timeout", "10000"));
|
||||
public static final Integer HTTP_CLIENT_CONNECT_TIMEOUT = Integer.parseInt(System.getProperty("httpclient.connect.timeout", "10000"));
|
||||
public static final Integer HTTP_CLIENT_CONNECTION_REQUEST_TIMEOUT = Integer.parseInt(System.getProperty("httpclient.connection-request.timeout", "10000"));
|
||||
|
||||
@Before
|
||||
public void initializeClient() {
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
|
||||
connectionManager.setMaxTotal(Math.max(1, MAX_THREADS / 10));
|
||||
connectionManager.setDefaultMaxPerRoute(connectionManager.getMaxTotal());
|
||||
connectionManager.setValidateAfterInactivity(10000);
|
||||
connectionManager.setDefaultSocketConfig(getDefaultSocketConfig());
|
||||
|
||||
client = HttpClients.custom()
|
||||
.setConnectionManager(connectionManager)
|
||||
.setDefaultCookieStore(new BasicCookieStore())
|
||||
.setDefaultRequestConfig(getDefaultRequestConfig())
|
||||
.setRedirectStrategy(new CustomRedirectStrategy())
|
||||
.build();
|
||||
}
|
||||
|
||||
protected SocketConfig getDefaultSocketConfig() {
|
||||
return SocketConfig.copy(SocketConfig.DEFAULT)
|
||||
.setSoTimeout(HTTP_CLIENT_SOCKET_TIMEOUT).build();
|
||||
}
|
||||
|
||||
protected RequestConfig getDefaultRequestConfig() {
|
||||
return RequestConfig.custom()
|
||||
.setSocketTimeout(HTTP_CLIENT_SOCKET_TIMEOUT)
|
||||
.setConnectTimeout(HTTP_CLIENT_CONNECT_TIMEOUT)
|
||||
.setConnectionRequestTimeout(HTTP_CLIENT_CONNECTION_REQUEST_TIMEOUT)
|
||||
.setCookieSpec(CookieSpecs.DEFAULT)
|
||||
.setRedirectsEnabled(true)
|
||||
.setRelativeRedirectsAllowed(true)
|
||||
.setCircularRedirectsAllowed(false)
|
||||
.setMaxRedirects(2)
|
||||
.build();
|
||||
}
|
||||
|
||||
public class CustomRedirectStrategy extends DefaultRedirectStrategy {
|
||||
|
||||
private final String[] REDIRECT_METHODS;
|
||||
|
||||
public CustomRedirectStrategy() {
|
||||
this.REDIRECT_METHODS = new String[]{
|
||||
HttpGet.METHOD_NAME,
|
||||
HttpPost.METHOD_NAME,
|
||||
HttpHead.METHOD_NAME,
|
||||
HttpDelete.METHOD_NAME,
|
||||
HttpOptions.METHOD_NAME
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRedirectable(String method) {
|
||||
for (final String m : REDIRECT_METHODS) {
|
||||
if (m.equalsIgnoreCase(method)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void closeClient() throws IOException {
|
||||
client.close();
|
||||
}
|
||||
|
||||
|
||||
public abstract class Runnable extends PerformanceTest.Runnable {
|
||||
|
||||
protected HttpClientContext context;
|
||||
|
||||
public Runnable() {
|
||||
this.context = HttpClientContext.create();
|
||||
this.context.setCookieStore(new BasicCookieStore());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package org.keycloak.testsuite.performance.metrics;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public interface ComputedMetric extends Metric {
|
||||
|
||||
public void reset();
|
||||
public void addValue(long value);
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.keycloak.testsuite.performance.metrics;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public interface ComputedMetrics extends Metrics<ComputedMetric> {
|
||||
|
||||
public void reset();
|
||||
public void addValue(String metric, long value);
|
||||
public Metrics computeMetrics();
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package org.keycloak.testsuite.performance.metrics;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public interface Metric {
|
||||
|
||||
public long getCount();
|
||||
public long getMin();
|
||||
public long getMax();
|
||||
public double getAverage();
|
||||
public double getStandardDeviation();
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package org.keycloak.testsuite.performance.metrics;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
* @param <T>
|
||||
*/
|
||||
public interface Metrics<T extends Metric> extends Map<String, T> {
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package org.keycloak.testsuite.performance.metrics.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import org.keycloak.testsuite.performance.metrics.ComputedMetric;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public class ArrayListMetric extends ArrayList<Long> implements ComputedMetric {
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addValue(long value) {
|
||||
add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCount() {
|
||||
return size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMin() {
|
||||
return Collections.min(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMax() {
|
||||
return Collections.max(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getAverage() {
|
||||
long sum = 0;
|
||||
for (long l : this) {
|
||||
sum += l;
|
||||
}
|
||||
return isEmpty() ? 0 : sum / size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getStandardDeviation() {
|
||||
double average = getAverage();
|
||||
long sumSquare = 0;
|
||||
for (long l : this) {
|
||||
sumSquare += l * l;
|
||||
}
|
||||
return isEmpty() ? 0
|
||||
: Math.sqrt(sumSquare / size() - (average * average));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package org.keycloak.testsuite.performance.metrics.impl;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import org.keycloak.testsuite.performance.metrics.ComputedMetric;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public final class MovingAverageMetric implements ComputedMetric {
|
||||
|
||||
private BigDecimal sum;
|
||||
private BigDecimal sumSquare;
|
||||
private long count;
|
||||
private long min;
|
||||
private long max;
|
||||
|
||||
public MovingAverageMetric() {
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() {
|
||||
this.sum = new BigDecimal(0);
|
||||
this.sumSquare = new BigDecimal(0);
|
||||
count = 0;
|
||||
min = Long.MAX_VALUE;
|
||||
max = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized double getAverage() {
|
||||
return count == 0 ? 0
|
||||
: sum.longValue() / count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized double getStandardDeviation() {
|
||||
double average = getAverage();
|
||||
return count == 0 ? 0
|
||||
: Math.sqrt(sumSquare.longValue() / count - (average * average));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void addValue(long value) {
|
||||
sum = sum.add(new BigDecimal(value));
|
||||
sumSquare = sumSquare.add(new BigDecimal(value * value));
|
||||
min = Math.min(min, value);
|
||||
max = Math.max(max, value);
|
||||
count++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Double.toString(getAverage());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package org.keycloak.testsuite.performance.metrics.impl;
|
||||
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang.builder.ToStringStyle;
|
||||
import org.keycloak.testsuite.performance.metrics.Metric;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public class Result implements Metric {
|
||||
|
||||
private final long count;
|
||||
private final long min;
|
||||
private final long max;
|
||||
private final double average;
|
||||
private final double standardDeviation;
|
||||
|
||||
public Result(long count, long min, long max, double average, double standardDeviation) {
|
||||
this.count = count;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.average = average;
|
||||
this.standardDeviation = standardDeviation;
|
||||
}
|
||||
|
||||
public Result(Metric metric) {
|
||||
this(
|
||||
metric.getCount(),
|
||||
metric.getMin(),
|
||||
metric.getMax(),
|
||||
metric.getAverage(),
|
||||
metric.getStandardDeviation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getAverage() {
|
||||
return average;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getStandardDeviation() {
|
||||
return standardDeviation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
}
|
||||
|
||||
public String toLogString() {
|
||||
return count + " " + min + " " + max + " " + average + " " + standardDeviation;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package org.keycloak.testsuite.performance.metrics.impl;
|
||||
|
||||
import java.util.TreeMap;
|
||||
import org.keycloak.testsuite.performance.metrics.ComputedMetrics;
|
||||
import org.keycloak.testsuite.performance.metrics.Metrics;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public class Results extends TreeMap<String, Result> implements Metrics<Result> {
|
||||
|
||||
public Results(ComputedMetrics metrics) {
|
||||
for (String metric : metrics.keySet()) {
|
||||
put(metric, new Result(metrics.get(metric)));
|
||||
}
|
||||
}
|
||||
|
||||
public String getHeader() {
|
||||
return "# Operation Count Min Max Average Standard-Deviation";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package org.keycloak.testsuite.performance.metrics.impl;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.keycloak.testsuite.performance.metrics.ComputedMetrics;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author tkyjovsk
|
||||
*/
|
||||
public class ResultsWithThroughput extends Results {
|
||||
|
||||
public static final Logger LOG = Logger.getLogger(ResultsWithThroughput.class);
|
||||
private final Map<String, Double> throughput;
|
||||
|
||||
public ResultsWithThroughput(ComputedMetrics metrics, long durationMillis) {
|
||||
super(metrics);
|
||||
throughput = new HashMap<>();
|
||||
for (String metric : keySet()) {
|
||||
throughput.put(metric, (double) get(metric).getCount() / durationMillis * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Double> getThroughput() {
|
||||
return throughput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Results: " + super.toString() + "\n"
|
||||
+ "Throughput: " + getThroughput();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader() {
|
||||
return super.getHeader() + " Throughput";
|
||||
|
||||
}
|
||||
|
||||
public void logResults() {
|
||||
LOG.info(getHeader());
|
||||
for (String metric : keySet()) {
|
||||
LOG.info(metric + " " + get(metric).toLogString() + " " + getThroughput().get(metric));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,30 @@
|
|||
log4j.rootLogger=INFO, DEFAULT
|
||||
|
||||
# TEST RUN
|
||||
|
||||
log4j.appender.DEFAULT=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.DEFAULT.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.DEFAULT.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] [%t] %m%n
|
||||
|
||||
log4j.logger.org.keycloak=OFF
|
||||
log4j.logger.org.keycloak.testsuite=INFO
|
||||
|
||||
# HtmlUnit
|
||||
log4j.logger.org.keycloak.testsuite.performance.htmlunit.HtmlUnitLoginLogoutPerfTest=${logging.loginlogout}
|
||||
log4j.logger.com.gargoylesoftware.htmlunit=OFF
|
||||
|
||||
# HttpClient
|
||||
log4j.logger.org.keycloak.testsuite.performance.httpclient.HttpClientLoginLogoutPerfTest=${logging.loginlogout}
|
||||
log4j.logger.org.keycloak.testsuite.performance.httpclient.HttpClientPerformanceTest$CustomRedirectStrategy=OFF
|
||||
|
||||
|
||||
# RESULTS
|
||||
|
||||
log4j.appender.RESULTS=org.apache.log4j.FileAppender
|
||||
log4j.appender.RESULTS.file=target/results.log
|
||||
log4j.appender.RESULTS.immediateFlush=true
|
||||
log4j.appender.RESULTS.append=true
|
||||
log4j.appender.RESULTS.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.RESULTS.layout.ConversionPattern=%d{HHmmss} %m%n
|
||||
|
||||
log4j.logger.org.keycloak.testsuite.performance.metrics.impl.ResultsWithThroughput=INFO, RESULTS
|
|
@ -0,0 +1,51 @@
|
|||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:xalan="http://xml.apache.org/xalan"
|
||||
xmlns:a="http://jboss.org/schema/arquillian"
|
||||
version="2.0"
|
||||
exclude-result-prefixes="xalan a">
|
||||
|
||||
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
|
||||
<xsl:strip-space elements="*"/>
|
||||
|
||||
<xsl:template match="/a:arquillian">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="node()|@*"/>
|
||||
|
||||
<container qualifier="app-server-remote" mode="${{app.server.mode}}" >
|
||||
<configuration>
|
||||
<property name="adapterImplClass">org.jboss.as.arquillian.container.remote.RemoteDeployableContainer</property>
|
||||
|
||||
<property name="managementAddress">${app.server.host}</property>
|
||||
<property name="managementPort">${app.server.management.port}</property>
|
||||
<property name="username">admin</property>
|
||||
<property name="password">admin</property>
|
||||
</configuration>
|
||||
</container>
|
||||
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="@*|node()">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="@*|node()" />
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
<app.server.mode>manual</app.server.mode>
|
||||
|
||||
<app.server.host>localhost</app.server.host>
|
||||
<app.server.port.offset>200</app.server.port.offset>
|
||||
<app.server.http.port>8280</app.server.http.port>
|
||||
<app.server.https.port>8643</app.server.https.port>
|
||||
|
@ -143,6 +144,7 @@
|
|||
|
||||
<app.server.mode>${app.server.mode}</app.server.mode>
|
||||
|
||||
<app.server.host>${app.server.host}</app.server.host>
|
||||
<app.server.port.offset>${app.server.port.offset}</app.server.port.offset>
|
||||
<app.server.http.port>${app.server.http.port}</app.server.http.port>
|
||||
<app.server.https.port>${app.server.https.port}</app.server.https.port>
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
<auth.server.home>${containers.home}/${auth.server.container}</auth.server.home>
|
||||
<auth.server.config.dir>${auth.server.home}</auth.server.config.dir>
|
||||
|
||||
<auth.server.host>localhost</auth.server.host>
|
||||
<auth.server.port.offset>100</auth.server.port.offset>
|
||||
<auth.server.http.port>8180</auth.server.http.port>
|
||||
<auth.server.events.http.port>8089</auth.server.events.http.port>
|
||||
|
@ -60,6 +61,8 @@
|
|||
<auth.server.jboss.skip.unpack>${auth.server.undertow}</auth.server.jboss.skip.unpack>
|
||||
<auth.server.jboss.startup.timeout>300</auth.server.jboss.startup.timeout>
|
||||
|
||||
<auth.server.remote>false</auth.server.remote>
|
||||
|
||||
<adapter.test.props/>
|
||||
<examples.home>${project.build.directory}/examples</examples.home>
|
||||
|
||||
|
@ -142,6 +145,7 @@
|
|||
<auth.server.home>${auth.server.home}</auth.server.home>
|
||||
<auth.server.java.home>${auth.server.java.home}</auth.server.java.home>
|
||||
|
||||
<auth.server.host>${auth.server.host}</auth.server.host>
|
||||
<auth.server.port.offset>${auth.server.port.offset}</auth.server.port.offset>
|
||||
<auth.server.http.port>${auth.server.http.port}</auth.server.http.port>
|
||||
<auth.server.events.http.port>${auth.server.events.http.port}</auth.server.events.http.port>
|
||||
|
@ -154,6 +158,8 @@
|
|||
<frontend.console.output>${frontend.console.output}</frontend.console.output>
|
||||
<backends.console.output>${backend.console.output}</backends.console.output>
|
||||
|
||||
<auth.server.remote>${auth.server.remote}</auth.server.remote>
|
||||
|
||||
<adapter.test.props>${adapter.test.props}</adapter.test.props>
|
||||
|
||||
<testsuite.constants>${testsuite.constants}</testsuite.constants>
|
||||
|
@ -176,6 +182,23 @@
|
|||
|
||||
<profiles>
|
||||
|
||||
<profile>
|
||||
<id>auth-server-remote</id>
|
||||
<properties>
|
||||
<auth.server>remote</auth.server>
|
||||
<auth.server.remote>true</auth.server.remote>
|
||||
<auth.server.undertow>false</auth.server.undertow>
|
||||
<auth.server.jboss.skip.unpack>true</auth.server.jboss.skip.unpack>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.wildfly</groupId>
|
||||
<artifactId>wildfly-arquillian-container-remote</artifactId>
|
||||
<version>${arquillian-wildfly-container.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>auth-server-wildfly</id>
|
||||
<properties>
|
||||
|
@ -866,6 +889,17 @@
|
|||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>no-offset</id>
|
||||
<properties>
|
||||
<auth.server.port.offset>0</auth.server.port.offset>
|
||||
<auth.server.http.port>8080</auth.server.http.port>
|
||||
<auth.server.https.port>8443</auth.server.https.port>
|
||||
<auth.server.management.port>9990</auth.server.management.port>
|
||||
<auth.server.management.port.jmx>9999</auth.server.management.port.jmx>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
Loading…
Reference in a new issue