diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml
index cf8209e919..5266731c2f 100644
--- a/testsuite/integration-arquillian/pom.xml
+++ b/testsuite/integration-arquillian/pom.xml
@@ -78,6 +78,11 @@
wildfly-arquillian-container-managed
${arquillian-wildfly-container.version}
+
+ org.wildfly
+ wildfly-arquillian-container-remote
+ ${arquillian-wildfly-container.version}
+
@@ -100,9 +105,9 @@
+ test-apps
servers
tests
- test-apps
diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/common/io.xsl b/testsuite/integration-arquillian/servers/app-server/jboss/common/io.xsl
new file mode 100644
index 0000000000..03d518a13e
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/app-server/jboss/common/io.xsl
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/common/mgmt-users.properties b/testsuite/integration-arquillian/servers/app-server/jboss/common/mgmt-users.properties
new file mode 100644
index 0000000000..b27edafd1d
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/app-server/jboss/common/mgmt-users.properties
@@ -0,0 +1 @@
+admin=c22052286cd5d72239a90fe193737253
diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml
index 30db67f386..1f47523452 100644
--- a/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml
+++ b/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml
@@ -124,6 +124,61 @@
+
+ io-worker-threads
+ process-resources
+
+ transform
+
+
+
+
+ ${app.server.jboss.home}/standalone/configuration
+
+ standalone.xml
+ standalone-ha.xml
+
+ ${common.resources}/io.xsl
+ ${app.server.jboss.home}/standalone/configuration
+
+
+ worker.io-threads
+ ${app.server.worker.io-threads}
+
+
+ worker.task-max-threads
+ ${app.server.worker.task-max-threads}
+
+
+
+
+
+
+
+
+
+
+ maven-resources-plugin
+
+
+ enable-jboss-mgmt-admin
+ process-resources
+
+ copy-resources
+
+
+ ${app.server.jboss.home}/standalone/configuration
+
+
+ ${common.resources}
+
+ mgmt-users.properties
+
+
+
+ true
+
+
diff --git a/testsuite/integration-arquillian/servers/app-server/pom.xml b/testsuite/integration-arquillian/servers/app-server/pom.xml
index 9e763fb98a..17d9a3d999 100644
--- a/testsuite/integration-arquillian/servers/app-server/pom.xml
+++ b/testsuite/integration-arquillian/servers/app-server/pom.xml
@@ -31,6 +31,8 @@
false
+ ${jboss.default.worker.io-threads}
+ ${jboss.default.worker.task-max-threads}
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/io.xsl b/testsuite/integration-arquillian/servers/auth-server/jboss/common/io.xsl
new file mode 100644
index 0000000000..03d518a13e
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/io.xsl
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/mgmt-users.properties b/testsuite/integration-arquillian/servers/auth-server/jboss/common/mgmt-users.properties
new file mode 100644
index 0000000000..b27edafd1d
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/mgmt-users.properties
@@ -0,0 +1 @@
+admin=c22052286cd5d72239a90fe193737253
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/mod_cluster.xsl b/testsuite/integration-arquillian/servers/auth-server/jboss/common/mod_cluster.xsl
new file mode 100644
index 0000000000..c5d983d50d
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/mod_cluster.xsl
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
index a64313e24f..fdc4df4033 100644
--- a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
@@ -200,9 +200,62 @@
org.codehaus.mojo
xml-maven-plugin
+
+
+ io-worker-threads
+ process-resources
+
+ transform
+
+
+
+
+ ${auth.server.home}/standalone/configuration
+
+ standalone.xml
+ standalone-ha.xml
+
+ ${common.resources}/io.xsl
+ ${auth.server.home}/standalone/configuration
+
+
+ worker.io-threads
+ ${auth.server.worker.io-threads}
+
+
+ worker.task-max-threads
+ ${auth.server.worker.task-max-threads}
+
+
+
+
+
+
+
maven-resources-plugin
+
+
+ enable-jboss-mgmt-admin
+ process-resources
+
+ copy-resources
+
+
+ ${auth.server.home}/standalone/configuration
+
+
+ ${common.resources}
+
+ mgmt-users.properties
+
+
+
+ true
+
+
+
maven-assembly-plugin
@@ -577,6 +630,25 @@
+
+ mod-cluster-configuration
+ process-resources
+
+ transform
+
+
+
+
+ ${auth.server.home}/standalone/configuration
+
+ standalone-ha.xml
+
+ ${common.resources}/mod_cluster.xsl
+ ${auth.server.home}/standalone/configuration
+
+
+
+
diff --git a/testsuite/integration-arquillian/servers/auth-server/pom.xml b/testsuite/integration-arquillian/servers/auth-server/pom.xml
index a3328bdaef..a10cee0628 100644
--- a/testsuite/integration-arquillian/servers/auth-server/pom.xml
+++ b/testsuite/integration-arquillian/servers/auth-server/pom.xml
@@ -29,6 +29,11 @@
pom
Auth Server
+
+ ${jboss.default.worker.io-threads}
+ ${jboss.default.worker.task-max-threads}
+
+
services
jboss
diff --git a/testsuite/integration-arquillian/servers/pom.xml b/testsuite/integration-arquillian/servers/pom.xml
index c800f629fe..288ef46337 100644
--- a/testsuite/integration-arquillian/servers/pom.xml
+++ b/testsuite/integration-arquillian/servers/pom.xml
@@ -47,6 +47,8 @@
6.2.1.redhat-084
+ 16
+ 128
diff --git a/testsuite/integration-arquillian/servers/wildfly-balancer/pom.xml b/testsuite/integration-arquillian/servers/wildfly-balancer/pom.xml
index 262299c678..a84f20ce52 100644
--- a/testsuite/integration-arquillian/servers/wildfly-balancer/pom.xml
+++ b/testsuite/integration-arquillian/servers/wildfly-balancer/pom.xml
@@ -31,6 +31,8 @@
${project.build.directory}/unpacked/wildfly-${wildfly.version}
+ ${jboss.default.worker.io-threads}
+ ${jboss.default.worker.task-max-threads}
@@ -70,6 +72,35 @@
org.codehaus.mojo
xml-maven-plugin
+
+ io-worker-threads
+ process-resources
+
+ transform
+
+
+
+
+ ${wildfly.balancer.home}/standalone/configuration
+
+ standalone.xml
+
+ src/main/xslt/io.xsl
+ ${wildfly.balancer.home}/standalone/configuration
+
+
+ worker.io-threads
+ ${wildfly.balancer.worker.io-threads}
+
+
+ worker.task-max-threads
+ ${wildfly.balancer.worker.task-max-threads}
+
+
+
+
+
+
configure-mod-cluster
process-resources
diff --git a/testsuite/integration-arquillian/servers/wildfly-balancer/src/main/xslt/io.xsl b/testsuite/integration-arquillian/servers/wildfly-balancer/src/main/xslt/io.xsl
new file mode 100644
index 0000000000..03d518a13e
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly-balancer/src/main/xslt/io.xsl
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/wildfly-balancer/src/main/xslt/mod_cluster.xsl b/testsuite/integration-arquillian/servers/wildfly-balancer/src/main/xslt/mod_cluster.xsl
index 2079fc90f4..18853541e0 100644
--- a/testsuite/integration-arquillian/servers/wildfly-balancer/src/main/xslt/mod_cluster.xsl
+++ b/testsuite/integration-arquillian/servers/wildfly-balancer/src/main/xslt/mod_cluster.xsl
@@ -17,18 +17,14 @@
-
-
+ exclude-result-prefixes="xalan">
-
+
@@ -36,7 +32,7 @@
-
+
@@ -44,23 +40,34 @@
-
+
-
-
-
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AppServerTestEnricher.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AppServerTestEnricher.java
index ea64fe24a1..75d5510abc 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AppServerTestEnricher.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AppServerTestEnricher.java
@@ -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) {
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java
index a7238c3a24..841f9039da 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/AuthServerTestEnricher.java
@@ -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) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml
index f1188a1137..bb603becba 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml
@@ -135,6 +135,18 @@
+
+
+ ${auth.server.remote}
+ org.jboss.as.arquillian.container.remote.RemoteDeployableContainer
+
+ ${auth.server.host}
+ ${auth.server.management.port}
+ admin
+ admin
+
+
+
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml
index 42780cf1e1..5b2a9cdcf9 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/pom.xml
@@ -36,6 +36,7 @@
${project.parent.basedir}/common
${app.server.home}/modules/system/add-ons/keycloak
+ managed
@@ -85,7 +86,8 @@
org.wildfly
- wildfly-arquillian-container-managed
+ wildfly-arquillian-container-${app.server.type}
+ ${arquillian-wildfly-container.version}
@@ -135,4 +137,4 @@
-
\ No newline at end of file
+
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/README.md b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/README.md
new file mode 100644
index 0000000000..a9bd7fde83
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/README.md
@@ -0,0 +1,2 @@
+#
+
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/pom.xml
new file mode 100644
index 0000000000..03ee852bb5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/pom.xml
@@ -0,0 +1,169 @@
+
+
+
+
+
+ 4.0.0
+
+
+ org.keycloak.testsuite
+ integration-arquillian-tests-adapters-jboss
+ 2.0.0.CR1-SNAPSHOT
+
+
+ integration-arquillian-tests-adapters-remote
+
+ Adapter Tests - JBoss - Remote
+
+
+ remote
+ remote
+ true
+ src/test/resources/xslt/arquillian.xsl
+
+ **/performance/htmlunit/**/*Test.java
+ **/performance/httpclient/**/*Test.java
+
+ INFO
+
+
+
+
+ commons-io
+ commons-io
+ 2.4
+
+
+
+
+
+
+ maven-dependency-plugin
+
+
+ copy-app-profile-jee-quickstart
+ generate-test-resources
+
+ copy
+
+
+
+
+ org.keycloak.quickstart
+ keycloak-quickstart-app-profile-jee
+ 0.5-SNAPSHOT
+ war
+
+
+ ${examples.home}
+ true
+
+
+
+
+
+ maven-enforcer-plugin
+
+
+
+ enforce
+
+
+
+
+ auth-server-remote
+
+
+
+
+
+
+
+
+ maven-surefire-plugin
+
+
+ ${exclude.htmlunit}
+ ${exclude.httpclient}
+
+
+ ${logging.loginlogout}
+
+
+
+
+
+
+
+
+ auth-server-remote
+
+
+
+ no-offset
+
+ 0
+ 8080
+ 8443
+ 9990
+ 9999
+
+
+
+ htmlunit
+
+ -
+
+
+
+ httpclient
+
+ -
+
+
+
+ debug
+
+ TRACE
+
+
+
+
+ maven-surefire-plugin
+
+
+ 1
+ 10
+ 0
+ 30
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/main/java/org/keycloak/testsuite/performance/page/AppProfileJEE.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/main/java/org/keycloak/testsuite/performance/page/AppProfileJEE.java
new file mode 100644
index 0000000000..799c8a97bd
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/main/java/org/keycloak/testsuite/performance/page/AppProfileJEE.java
@@ -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;
+ }
+
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/LoginLogoutParameters.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/LoginLogoutParameters.java
new file mode 100644
index 0000000000..0ca09d2994
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/LoginLogoutParameters.java
@@ -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";
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/OperationTimeoutException.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/OperationTimeoutException.java
new file mode 100644
index 0000000000..deb5026f4e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/OperationTimeoutException.java
@@ -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;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/PerformanceTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/PerformanceTest.java
new file mode 100644
index 0000000000..a4278e131a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/PerformanceTest.java
@@ -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 resultsList = new ArrayList<>();
+ protected List 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;
+
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/PerformanceTestMetrics.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/PerformanceTestMetrics.java
new file mode 100644
index 0000000000..1ebaea33f2
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/PerformanceTestMetrics.java
@@ -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 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());
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/RepeatRunnable.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/RepeatRunnable.java
new file mode 100644
index 0000000000..f62d19608c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/RepeatRunnable.java
@@ -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();
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/Timer.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/Timer.java
new file mode 100644
index 0000000000..8edb326737
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/Timer.java
@@ -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;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/htmlunit/HtmlUnitLoginLogoutPerfTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/htmlunit/HtmlUnitLoginLogoutPerfTest.java
new file mode 100644
index 0000000000..b2790985fa
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/htmlunit/HtmlUnitLoginLogoutPerfTest.java
@@ -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 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");
+ }
+
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/htmlunit/HtmlUnitPerformanceTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/htmlunit/HtmlUnitPerformanceTest.java
new file mode 100644
index 0000000000..ae9dd5070f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/htmlunit/HtmlUnitPerformanceTest.java
@@ -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();
+ }
+
+ }
+
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/httpclient/HttpClientLoginLogoutPerfTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/httpclient/HttpClientLoginLogoutPerfTest.java
new file mode 100644
index 0000000000..2a6e301b38
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/remote/src/test/java/org/keycloak/testsuite/performance/httpclient/HttpClientLoginLogoutPerfTest.java
@@ -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 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 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 = "
+
+ no-offset
+
+ 0
+ 8080
+ 8443
+ 9990
+ 9999
+
+
+